在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
/setPrototypeOf
isExtensible
/preventExtensions
ownKeys
/getOwnPropertyDescriptor
defineProperty
/deleteProperty
get
/set
/has
apply
/construct
总结
Proxy
相比于 defineProperty
的优势:
- 基于
Proxy
和Reflect
,可以原生监听数组,可以监听对象属性的添加和删除 - 不需要深度遍历监听:判断当前
Reflect.get
的返回值是否为Object
- 只在
getter
时才对对象的下一层进行劫持(优化了性能)
而Proxy
除了兼容性不足以外,其他方面的表示都优于Object.defineProperty
。