「这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战」
数据代理的含义
数据代理的另一个名称是数据劫持,在访问或者修改对象的某个属性时,数据劫持可以拦截这个行为并进行额外的操作或者修改返回的结果。vue的核心就是数据代理,代理使得数据在访问时进行依赖收集,在修改更新时对依赖进行更新。Vue.js 实现响应式的核心是利用了 ES5 的 Object.defineProperty或ES6的Proxy。
-
Object.defineProperty
Object.defineProperty
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
Object.defineProperty(obj, prop, descriptor)
obj 是要在其上定义属性的对象;prop 是要定义或修改的属性的名称;descriptor 是将被定义或修改的属性描述符。
var x = {}
var value;
Object.defineProperty(x, 'hello', {
get() {
console.log('获取值')
return value
},
set(v) {
console.log('设置值')
value = hahaha
}
})
x.a = 'world'
// 设置值
console.log(x.a)
// 获取值
// 'hahaha'
Object.defineProperty的get和set方法是对对象进行监测并响应变化。那么数组类型是否也可以监测呢,参照监听对象的步骤,我们看一下Object.defineProperty是否可以对数组的数据进行监控拦截。
var arry = [1,2,3];
arry.forEach((item, index) => {
Object.defineProperty(arry, index, {
get() {
console.log('数组被getter拦截')
return item
},
set(value) {
console.log('数组被setter拦截')
return item = value
}
})
})
arry[1] = 4;
console.log(arry )
// 结果
数组被setter拦截
数组被getter拦截
从上述代码中可以看到使用Object.defineProperty进行数据代理的弊端,即已知长度的数组是可以通过索引属性来设置属性的访问器属性的。 但是数组的添加确无法进行拦截,不管是通过arr.push()还是arr[10] = 10添加的数据,数组所添加的索引值并没有预先加入数据拦截中,所以自然无法进行拦截处理。
另外如果需要拦截的对象属性嵌套多层,如果没有递归去调用Object.defineProperty进行拦截,深层次的数据也依然无法监测。
-
Proxy
es6引入了Proxy的概念,它是真正在语言层面对数据拦截的定义。和Object.defineProperty一样,Proxy可以修改某些操作的默认行为。
不同点是Proxy针对目标对象会创建一个新的实例对象,并将目标对象代理到新的实例对象上。
其本质的区别是后者会创建一个新的对象对原对象做代理,外界对原对象的访问,都必须先通过这层代理进行拦截处理。而拦截的结果是我们只要通过操作新的实例对象就能间接的操作真正的目标对象了。
var obj = {}
var newObj = new Proxy(obj, {
get(target, key, receiver) {
console.log('获取值')
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
console.log('设置值')
return Reflect.set(target, key, value, receiver)
}
})
newObj.a = '代理'
console.log(obj)
// 结果
设置值
{a: "代理"}
上面的get,set是Proxy支持的拦截方法,而Proxy 支持的拦截操作有13种之多,具体可以参照ES6-Proxy文档。
var arry = [1, 2, 3]
let obj = new Proxy(arry, {
get: function (item, key, receiver) {
// console.log("获取数组元素" + key);
return Reflect.get(item, key, receiver);
},
set: function (item, receiver) {
console.log('设置数组');
return Reflect.set(item,key, receiver);
}
})
// 1. 改变已存在索引的数据
obj[2] = 3
// result: 设置数组
// 2. push,unshift添加数据
obj.push(4)
// result: 设置数组 * 2 (索引和length属性都会触发setter)
// // 3. 直接通过索引添加数组
obj[5] = 5
// result: 设置数组 * 2
// // 4. 删除数组元素
obj.splice(1, 1)
以上代码使用Proxy完美的解决了数组的监听检测问题,针对数组添加数据,删除数据的不同方法,代理都能很好的拦截处理。另外Proxy也很好的解决了深层次嵌套对象的问题。