这个问题绝大多数人都被问起过,可是怎么答感觉都不太满意,但其实你只要理解过后,一句话就可以回答出来,而且是极度的精准。
1.Proxy
MDN上的解释:用于创建一个对象的代理,可以拦截和重定义一个对象的基本方法,那么什么叫做对象的基本方法呢?先看下面对象的基本操作。
let obj = {};
// 添加一个属性
obj.a = 3; // 相当于调用了[[Set]]
// 读取一个属性
obj.a; // 相当于调用了[[Get]]
// 设置对象的原型
Object.setPrototypeOf(obj, {a:3}); // 相当于调用了 [[SetPrototypeOf]]
// 读取对象的原型
Object.getPrototypeOf(obj); // 相当于调用了 [[GetPrototypeOf]]
// 遍历对象的属性,相当于调用了对象的 [[getOwnPropertyKeys]]
for(const key in obj){
}
上面的基本操作其实本质上会转换成调用对象的内部方法,比如obj.a=3 会调用对象的内部方法 [[Set]],这方法在外面是访问不到的,只有在内部才能访问到。也就是说,平时我们js语法层面,或者是API层面,去对对象进行基本操作,最终在底层都是在调用对象的内部方法。换句话说,你对对象的基本操作,是逃不过这个基本方法的。在ES262文档里面可以看到所有的内部方法:
这里的内部方法,就是Proxy解释中说的基本方法。数组本质上也是一个对象,区别于常规对象,普遍把数组叫做异质对象,常规对象和异质对象的不同体现在[[DefineOwnProperty]]这个内部方法上。Proxy重定义对象基本方法的同时也可以对这些基本方法进行拦截,换句话说,Proxy可以拦截对象的一些操作。你可能会问,函数也能拦截吗?答案是可以的函数在内部也是一个对象,只不过多了两个内部方法[[Call]]和[[Construct]],你通过new调用而已。
2.DefineProperty
MDN解释:会直接在一个对象上定义一个新属性,或修改其现有属性,并返回此对象。
const arr = [1, 2, 3];
// 往数组里面加一项,会导致数组的长度也变化
arr.push(1);
// 尝试使用DefineProperty拦截一个数组的length变化
Object.defineProperty(arr, 'length', {
get(){
console.log('get', 'length');
return 2;
},
set(value){
console.log('set', 'length', value);
}
})
这个时候你会发现,vue2没办法直接拦截数组原型,只能在数组本身查找数组原型的过程中重写push、shift、unshift这些方法。更改数组的原型链,你在vue2中调用的push方法,其实调用的是重写过后的push方法,这样就可以进行拦截。
总结: Proxy拦截的是针对对象的所有基本方法,他的拦截是全面的,没有任何遗漏;而defineProperty只是众多基本方法其中之一,作用是对现有对象的拦截,拦截面是不全面的。