Proxy 的优势如下:
- Proxy 可以直接监听数组的变化
- Proxy 可以直接监听对象而非属性
- Proxy 有 13 种拦截方法,比 Object.defineProperty 要更加丰富的多
Object.defineProperty 的优势如下:
- 兼容性好
Object.defineProperty (obj, prop, descriptor) 的问题主要有三个:
- 无法监听数组的变化
- 必须遍历对象的每个属性
- 必须深层遍历嵌套的对象
(1)无法监听数组的变化
Vue 把会修改原来数组的方法定义为变异方法。
变异方法例如 push、pop、shift、unshift、splice、sort、reverse等,是无法触发 set 的。
非变异方法,例如 filter,concat,slice 等,它们都不会修改原始数组,而会返回一个新的数组。
Vue 的做法是把这些变异方法重写来实现监听数组变化。
const aryMethods = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse',
]
const arrayAugmentations = {}
aryMethods.forEach((method) => {
// 这里是原生 Array 的原型方法
let original = Array.prototype[method]
// 将 push, pop 等封装好的方法定义在对象 arrayAugmentations 的属性上
// 注意:是实例属性而非原型属性
arrayAugmentations[method] = function () {
console.log('我被改变啦!')
// 调用对应的原生方法并返回结果
return original.apply(this, arguments)
}
})
let list = ['a', 'b', 'c']
// 将我们要监听的数组的原型指针指向上面定义的空数组对象
// 这样就能在调用 push, pop 这些方法时走进我们刚定义的方法,多了一句 console.log
list.__proto__ = arrayAugmentations
list.push('d') // 我被改变啦!
// 这个 list2 是个普通的数组,所以调用 push 不会走到我们的方法里面。
let list2 = ['a', 'b', 'c']
list2.push('d') // 不输出内容
(2)必须遍历对象的每个属性
使用 Object.defineProperty 多数情况下要配合 Object.keys 和遍历,于是就多了一层嵌套。
并且由于遍历的原因,假如对象上的某个属性并不需要“劫持”,但此时依然会对其添加“劫持”。
Object.keys(obj).forEach((key) => {
Object.defineProperty(obj, key, {
// ...
})
})
(3)必须深层遍历嵌套的对象
当一个对象为深层嵌套的时候,必须进行逐层遍历,直到把每个对象的每个属性都调用 Object.defineProperty() 为止。
以上。