Proxy
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
// 语法
const myProxy = new Proxy(target, handle)
// target 要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
// handler 一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。
Object.defineProperty缺陷
- 对象属性新增删除无法检测,导致视图不更新
- 数组的索引直接设置一个数组项无法检测,例:
vm.items[indexOfItem] = newValue
,新增索引也无法检测,导致视图不更新
原因:
- Object.defineProperty 无法检测到对象属性的添加和删除,是因为这个方法只能监听已存在的属性。如果要解决需要使用Vue提供的全局API-$set,其本质也是给新增的属性手动 Observer。
- 数组的索引直接设置一个数组项无法检测,并不是 defineProperty 的错,而是vue的的设计在性能的权衡。
- 新增索引的确是 defineProperty 做不到的,所有就有了 vue 对数组的重写方法,和$set 一样,手动 observer
vue 对数组的重写方法是指:
重新定义数组的 push,pop,shift,unshift,splice,sort,reverse 方法,调用以上方法时 key 的订阅者列表会通知订阅者们"值已改变"。如果调用 pus,unshift,splice 方法,递归处理新增的项。
二者的区别
- 使用 Object.defineProperty 需要遍历对象的每一个属性,对于性能会有一定的影响
- Proxy 作为新标准将受到浏览器厂商重点持续的性能优化
- Proxy 能观察的类型比 definedProperty 更丰富
- Proxy 不兼容IE,defineProperty 能支持到IE9
- Object.definedProperty 是劫持对象的属性,新增元素需要再次 definedProperty。而 Proxy 劫持的是整个对象,不需要做特殊处理
- 使用 defineProperty 时,我们修改原来的 obj 对象就可以触发拦截,而使用 proxy,就必须修改代理对象(new Proxy),即 Proxy 的实例才可以触发拦截
Proxy常见方法
方法 | 描述 |
handler.has() | in 操作符的捕捉器 |
handler.get(target, propKey, ?receiver(实例本身)) | 属性读取操作的捕捉器。 |
handler.set(handler.set(target, propKey, value ?receiver(实例本身)) | 属性设置操作的捕捉器。 |
handler.deleteProperty() | delete 操作符的捕捉器。 |
handler.ownKeys() | Object.getOwnPropertyNames 方法和 Object.getOwnPropertySymbols 方法的捕捉器。 |
handler.apply() | 函数调用操作的捕捉器。 |
handler.construct() | new 操作符的捕捉器 |