在vue项目开发中,经常需要做变量监听。ES5 使用的方法是Object.defineProperty,ES6使用的方法是Proxy。
Object.defineProperty
用法如下:
Object.defineProperty(obj, prop, option)入参用法:
- obj:代理对象;
- prop:代理对象中的key;
- option:配置对象,
get、set都在其中配置;
例子:
var obj = {
name: 'sorryhc'
}
var rocordName = 'sorryhc';
Object.defineProperty(obj, 'name', {
enumerable: true,
configurable:true,
set: function(newVal) {
rocordName = newVal
console.log('set: ' + rocordName)
},
get: function() {
console.log('get: ' + rocordName)
return rocordName
}
})
obj.name = 'sorrycc' // set: sorrycc
console.log(obj.name) // get: sorrycc缺陷
如果学习过Vue2源码的同学可能比较熟,基于下面的缺陷,也是出现了$set、$get的用法。
- IE8 及更低版本 IE 是不支持的
- 无法检测到对象属性的新增或删除
- 如果修改数组的
length(Object.defineProperty不能监听数组的长度),以及数组的push等变异方法是无法触发setter的
Proxy
用法如下:
const obj = new Proxy(target, handler)其中:
target:要使用Proxy包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)handler:一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理obj的行为
例子
const handler = {
get: function(target, name){
return name in target ? target[name] : 'no prop!'
},
set: function(target, prop, value, receiver) {
target[prop] = value;
console.log('property set: ' + prop + ' = ' + value);
return true;
}
};
var user = new Proxy({}, handler)
user.name = 'sorryhc' // property set: name = sorryhc
console.log(user.name) // sorryhc
console.log(user.age) // no prop!
复制代码并且Proxy提供了更丰富的代理能力:
getPrototypeOf/setPrototypeOfisExtensible/preventExtensionsownKeys/getOwnPropertyDescriptordefineProperty/deletePropertyget/set/hasapply/construct
总结
Proxy 相比于 defineProperty 的优势:
- 基于
Proxy和Reflect,可以原生监听数组,可以监听对象属性的添加和删除 - 不需要深度遍历监听:判断当前
Reflect.get的返回值是否为Object - 只在
getter时才对对象的下一层进行劫持(优化了性能)
而Proxy除了兼容性不足以外,其他方面的表示都优于Object.defineProperty。
