惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

宝玉的分享
宝玉的分享
小众软件
小众软件
J
Java Code Geeks
I
InfoQ
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
腾讯CDC
L
LangChain Blog
博客园 - 司徒正美
量子位
Y
Y Combinator Blog
C
Check Point Blog
T
Tailwind CSS Blog
D
DataBreaches.Net
Blog — PlanetScale
Blog — PlanetScale
N
Netflix TechBlog - Medium
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
F
Fortinet All Blogs
云风的 BLOG
云风的 BLOG
A
About on SuperTechFans
B
Blog RSS Feed
酷 壳 – CoolShell
酷 壳 – CoolShell
大猫的无限游戏
大猫的无限游戏
V
V2EX
阮一峰的网络日志
阮一峰的网络日志

Aimee's Blog

秒杀怎么防机器人:验证码之外的几道防线 防薅羊毛怎么做:号码风控、设备指纹与分层策略 接口限流怎么选:固定窗口、滑动窗口、令牌桶 批量打包下载怎么设计:流式 ZIP 与异步任务 图片处理怎么做:异步处理与 CDN 实时参数 大文件上传怎么设计:分片、断点续传、秒传 报表可重跑的设计:快照、水位线与幂等 UV 统计为什么有误差:精确计数与近似计数怎么选 大数据量导出怎么做:流式写入与异步任务 排行榜的两类需求,难度差了一个数量级 内容审核怎么选:先审后发,还是先发后审 实时热榜怎么设计:Redis ZSet 与热度分值 互关状态怎么保持同步:双向关系的维护与缓存失效 并发点赞的计数设计:原子操作与最终对账 删了父评论,子评论怎么处理——先想清楚用户看到的是什么 定时消息任务触发了两次 消息量大,写扩散还是读扩散——接到这个需求先问清楚规模 同一条消息推送了三次 活动结束了,用户还在收短信 接到注销需求,先问两个问题 微服务与服务拆分:何时拆、怎么拆 异步与事件驱动架构:把协作从「打电话」改成「发消息」 高可用设计:怎么让系统尽量不宕机 可扩展性设计:怎么让系统加机器就能扛更多 缓存架构:多级缓存怎么搭 高并发三板斧:限流、熔断、降级 架构设计到底在设计什么 —— 从单体到微服务的演进 服务成本账:一个服务一个月烧多少钱 可观测性:线上出问题怎么查 API 设计:好接口长什么样
Proxy 和 Object.defineProperty
Aimee · 2021-10-26 · via Aimee's Blog

Proxy

定义

Proxy主要用于改变对象属性的默认访问行为,在目标对象之前架设一层"拦截",外界对该属性的访问,都需先通过这一层拦截,因此提供了一层机制,可对外界的访问进行过滤和改写。 ​

用法:

/*
 * target: 目标对象
 * handler: 配置对象,用来定义拦截的行为
 * oproxy: Proxy构造器的实例
 */
const obj = {}
const handler = {
	get(target, key,receiver){ // 拦截对象属性的读取
		console.log('get')
    return Reflect.get(target, key,receiver)
	},
  set(target, key, value, receiver){ // 拦截对象属性的设置
		console.log('set')
    return Reflect.set(target, key, value, receiver)
	},
  has(target, key){ // 拦截propKey in proxy 操作
    console.log('has')
  	return Reflect.has(target, key)
  },
  deleteProperty(target, key){// 拦截delete proxy[propKey] 操作 返回一个布尔值
    console.log('deleteProperty')
  	return Reflect.deleteProperty(target, key)
  },
  defineProperty(target, key, desc){ // 拦截Object.defineProperty(proxy, key, desc)/Object.defineProperties(proxy,decs)  返回一个布尔值
  	return Reflect.defineProperty(target, key,desc)
  },
  construct(target, args){ // 拦截 proxy实例作为构造函数实例化的操作,比如 new proxy(...args)
  	 console.log('construct')
    return Reflect.construct(target, args)
  },
  ownKeys(target){ //getOwnPropertyNames(proxy) / getOwnPropertySymbols(proxy)/for...in/Object.keys(proxy),返回一个数组。Object.keys,而Object.keys()返回结果仅包括目标对象可遍历的属性
  	console.log('ownKeys')
    return Reflect.ownKeys(target)
  },
  getOwnPropertyDescriptor(target,key){//拦截Object.getOwnPropertyDescriptor(), 返回属性的的描述对象
  	console.log('getOwnPropertyDescriptor')
    return Reflect.getOwnPropertyDescriptor(target,key)
  },
  preventExtensions(target){ // 拦截Object.preventExtensions(proxy), 返回一个布尔值
  	console.log('preventExtensions')
    return Reflect.preventExtensions(target)
  },
  getPrototypeOf(target){ // 拦截Object.getPrototyOf(proxy), 返回一个对象
  	console.log('getPrototypeOf')
    return Reflect.getPrototypeOf(target)
  },
  setPrototypeOf(target, proto){// 拦截Object.setPrototyOf(proxy,proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
  	console.log('setPrototypeOf')
    return Reflect.setPrototypeOf(target, proto)
  },
  apply(target,object ,args){//拦截proxy实例作为函数时的调用,比如proxy(...args)/proxy.call(object, ...args)/proxy.apply(...args)
  	console.log('apply')
    return Reflect.apply(target, object ,args)
  },
  isExtensible(target){// 拦截Object.isExtensible(), 返回一个布尔值
  	console.log('isExtensible')
    return Reflect.isExtensible(target)
  } 
  
}
const oproxy = new Proxy(obj, handler)
obj.a = 1
console.log(oproxy.a)// 

Proxy代理的对象属性是外层的,如果需要属性值对象进行代理,可以通过递归设置代理。

设置内层属性递归代理

const obj = {
	a: 1,
  objb: {
		c: 2
  }
}
const handler = {
	get(target, key, receiver){
    console.log('get')
    const value = target[key]
  	if(value !== 'null' && typeof value === 'object'){
    	return new Proxy(value, handler)
    }
    return Reflect.get(target, key, receiver)
  },
  set(target, key, value, receiver){
  	return Reflect.set(target, key, value, receiver)
  }
}

const oproxy = new Proxy(obj, handler)
oproxy.objb.c
// 输出
// get
// get
// 2

Object.defineProperty

定义

Object.defineProperty()直接在对象定义新属性,或者修改对象上的现有属性,并返回该对象。 ​

用法:

/*
* obj 要在其上定义或修改属性的对象
* prop 要定义或修改的属性的名称或者Symbol
* descriptor 定义或修改的属性描述符
*/
Object.defineProperty(obj, prop, descriptor)

描述符: 分为数据描述符和存取描述符,一个描述符只能是其中的一个,不能同时是两者。

  • 数据描述符:有值的属性,该值是否可重写。
  • 存取描述符:由getter 和setter函数所描述的属性。

共享属性: 1、configurable 表示可配置的,默认为false。当为true时,该属性的描述符才可以被改变,同时该属性可以被删除。 ​

2、enumrable 表示可枚举的,默认为false。当为true时,该属性才会在对象枚举时枚举到。 ​

数据描述符特有属性: 1、value 表示属性对应的值,默认为undefined 2、writable 表示属性是否可修改的,默认为false。只有值为true时,属性的值才可以被修改运算符修改成功。

const obj = {}
Object.defineProperty(obj, 'name', {
	configurable: true,
  enumrable: true,
  value: 'jack',
  writable: true
})

存取描述符特有属性: **1、get ** 当获取属性值的时候就会执行getter函数,该函数返回一个值, 默认为undefined 2、set 当设置属性值的时候就会执行setter函数

const obj = {}
Object.defineProperty(obj, 'name',{
  configurable: true,
  enumrable: true,
  get(){
    console.log('get--name')
		return 'jack'
	},
	set(value){
		console.log('set---name')
    return value
	}
})

Proxy和Object.defineProperty 对比:

  • Proxy是对整个对象的代理,而Object.defineProperty是代理对象的单个属性。
  • 对象上新增属性,Proxy可以监听到,而Object.defineProperty不可以
  • 数组新增修改,Proxy可以监听到,而Object.defineProperty不可以
  • 若对象内部的所有属性均要递归监听,Proxy可以只在调用的时候监听,而Object.defineProperty需要一次全部实现监听,性能比Proxy差
  • Proxy不兼容ie, Object.defineProperty不兼容ie8以下
  • Proxy使用上比Object.defineProperty更方便