Object.defineProperty 只能劫持对象的某一个属性,不能对整个对象进行劫持,如果需要监听某一个对象的所有属性,需要遍历对象的所有属性并对其进行劫持来进行监听。
const data = {
a: 1,
b: 2
};
Object.keys(data).forEach(key => {
let oldValue = data[key]
Object.defineProperty(data, key, {
get() {
console.log('get')
return oldValue
},
set(value) {
console.log('set')
oldValue = value
}
})
})
console.log(data.a) // 1
data.b = 3
console.log(data.b) // 3
console.log(data) // { a: 1, b: 3 }
const data = {
a: 1,
b: 2
};
const proxyData = new Proxy(data, {
get(target, prop) {
console.log('get')
return Reflect.get(target, prop)
},
set(target, prop, value) {
console.log('set')
return Reflect.set(target, prop, value) // Reflect通过代理对象更改目标对象的属性值
}
})
console.log(data.a) // 1
console.log(proxyData.a) // 1
data.a = 2
console.log(data.a) // 2
console.log(proxyData.a) // 2
proxyData.b = 3
console.log(data.b) // 3
console.log(proxyData.b) // 3
console.log(data) // { a: 1, b: 3 }
console.log(proxyData) // { a: 1, b: 3 }
设置代理对象的属性后,原始对象和代理对象都发生了变化,但是获取原始对象的属性不会触发getter,只有访问代理对象的属性才能触发getter。
data.c = 3;
对象新增属性的时候,Object.defineProperty没有对新增的属性进行劫持,监听不到对象属性的新增。
proxy是对整个对象进行代理,能监听到对象属性的新增。
无法监听对象删除属性
proxy有专门针对属性删除的方法deleteProperty,可以在对象属性被删除时触发。
const data = {
a: 1,
b: 2
};
const proxyData = new Proxy(data, {
get(target, prop) {
console.log('get')
return Reflect.get(target, prop)
},
set(target, prop, value) {
console.log('set')
return Reflect.set(target, prop, value) // Reflect通过代理对象更改目标对象的属性值
},
deleteProperty(target, prop) {
console.log('delete')
Reflect.deleteProperty(target, prop)
return true
}
})
delete proxyData.b
console.log(data)
无法监听到数组修改;但是Vue2中重写了push、pop、shift、unshift、splice、sort、reverse这七个数组方法,以达到监听的目的。但依然存在以下标方式修改数组值时响应式失效的情况,针对整个问题也提供了$set方法,即:this.$set(arr,index,value)。
proxy可以监听到数组修改,且不需要$set函数以及重写数组方法。
通过循环遍历对象属性的方式来进行监听,耗性能。
一次对整个对象进行监听,另外 Proxy 的拦截也是懒处理行为,如果没有访问嵌套对象,那么也不会实施拦截,这就让初始化的速度和内存占用都改善了。