blog
blog copied to clipboard
Object.defineProperty 与 Proxy
Object.definedProperty
在 vue2.x 版本中使用 Object.definedProperty 来劫持数据,实现数据双向绑定。我们来实现一个简单的数据劫持:
function observer(obj) {
if (typeof obj === 'object') {
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
listener(obj, key);
}
}
}
}
function listener(obj, key) {
let curValue = obj[key];
observer(curValue); // 如果curValue是对象,进入递归
Object.defineProperty(obj, key, {
get() {
console.log('get-->', curValue);
return curValue;
},
set(newVal) {
console.log('set-->', newVal);
curValue = newVal;
}
});
}
上面写了个简单版的数据劫持,现在我们来测试一下:
const obj = {
name: 'Jack',
age: 20
};
observer(obj)
现在我们将 obj.name
改为 Tom
:
灰常好,set 方法执行了,我们在更改数据前,劫持了数据。
我们再看看获取 name:
灰常好,get 方法也执行了,我们在获取数据前,劫持了数据。
看上去很美好,但 Object.definedProperty 并不是完美的,它在有些情况下无法劫持数据:
- 对删除和新增的属性无法监听到
- 数组的变化无法监听,虽然可以触发 get,但无法触发 set 的劫持
- 如果对象过大、层级过深,那么遍历的时间会更久,引发性能问题
Proxy
Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
语法
const p = new Proxy(target, handler)
- target: 要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
- handler: 一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。
还记得 Object.defineProperty 的语法吗:
Object.defineProperty(obj, key, options);
从语法上就能发现一个最大的不同点:Object.defineProperty 监听的是对象的属性,而 Proxy 监听的是整个对象
所以我们不需要遍历对象,而是直接监听对象:
const obj = {
arr: []
}
const handler = {
get(target, key, receiver){
console.log('get->', target[key])
if(typeof target[key] === 'object' && target[key] !== null) {
return new Proxy(target[key], handler)
}
return target[key]
},
set(target, key, value){
console.log('set->', key, value)
return Reflect.set(target, key, value)
}
}
const p = new Proxy(obj, handler)
上面我们监听的是一个空的对象,我们直接添加属性看看:
p.name = 'Jack';
这里注意,我们代理的是 obj
,返回的是 p
,所以要对 p 进行操作:
真好,set 触发了。
我们再看看获取 name
:

真好,get 也触发了。
我们再看看数组:

都可以非常好的监听到 get 、set,太强大了
关于 Proxy 的更多用法和说明,请看 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy