Object.defineProperty 和 new Proxy 都是 JavaScript 中用于对对象进行拦截和处理的机制,但它们有不同的优缺点。
Object.defineProperty 是一个用于定义或修改对象属性的方法。它可以通过设置属性的配置对象来实现对属性的拦截和修改。它的优点是它是原生的 JavaScript 方法,因此在所有支持 JavaScript 的环境中都可以使用。它可以用来实现属性值的访问控制、属性的计算和依赖追踪等功能。然而,它的缺点是它只能拦截对对象属性的直接访问,不能拦截对对象方法的调用,也不能拦截数组的操作。
Object.defineProperty 的设计初衷,并不是为了监听劫持一个对象中所有属性的
new Proxy 是 ES6 中引入的一个用于创建代理对象的构造函数。代理对象可以拦截并重定义基础对象的操作。它的优点是它可以拦截更多类型的操作,包括属性的读取、写入、删除、方法调用和构造函数调用等。它还提供了一些附加功能,如陷阱函数和代理行为控制。然而,它的缺点是它只能在支持 ES6 的环境中使用,并且在某些情况下可能会导致性能下降。
Object.defineProperty
const obj = {};
Object.defineProperty(obj, 'name', {
value: 'John',
writable: false,
enumerable: true,
configurable: true
});
console.log(obj.name); // 输出 "John"
obj.name = 'Jane'; // 由于 writable 属性被设置为 false,赋值操作无效
console.log(obj.name); // 输出 "John"
let obj10 = {};
Object.defineProperty(obj10, 'name', {
get: function () {
return this.value;
},
set: function (newVal) { //设置值(说明有数据更新)
this.value = newVal;
}
});
console.log(obj10.name); // undefined
obj10.name = '李四'; // 给obj.name赋新值
console.log(obj10.name); // 李四
如果一个描述符同时拥有 value 或 writable 和 get 或 set 键,则会产生一个异常测试代码:
var obj = {}
// value和 get 只能同时用一个
Object.defineProperty(obj, 'name', {
value: '孙悟空',
get: function() {
console.log('get')
return this.value
}
})
// writable和set只能同时用一个
Object.defineProperty(obj, 'name', {
writable: true,
set: function(newValue) {
console.log('set')
}
})
Proxy
基本语法如下:
/*
* target: 目标对象
* handler: 配置对象,用来定义拦截的行为
* proxy: Proxy构造器的实例
*/
var proxy = new Proxy(target,handler)
Proxy也只代理外面一层属性,需要递归代理属性嵌套的对象属性
let obj = {a: 3, b: { c: 3 }};
let handler = {
get:function(obj, prop){
const v = Reflect.get(obj, prop);
return v; // 返回obj[prop]
},
set(obj, prop, value){
return Reflect.set(obj, prop, value); // 设置成功返回true
}
};
let p=new Proxy(obj,handler);
p.a//会触发get方法
let obj = {a: 3, b: { c: 3 }};
let handler = {
get:function(obj, prop){
const v = Reflect.get(obj, prop);
if(v !== null && typeof v === 'object'){
return new Proxy(v,handler);//代理内层
}else{
return v; // 返回obj[prop]
}
},
set(obj, prop, value){
return Reflect.set(obj, prop, value);//设置成功返回true
}
};
let p=new Proxy(obj,handler);
p.a//会触发get方法
p.b.c//会先触发get方法获取p.b,然后触发返回的新代理对象的.c的set。
什么样的 num 可以满足 (num === 1 && num === 2 && num === 3) === true 呢?(注意是 3 个 =,也就是严格相等)
解决:每次访问 num 返回的值都不一样,那么肯定会想到数据劫持
let current = 0;
Object.defineProperty(window, 'num', {
get () {
current++
console.log(current)
return current
}
})
console.log(num === 1 && num === 2 && num === 3) // true
总结
1、Proxy 是对整个对象的代理,而 Object.defineProperty 只能代理某个属性
2、对象上新增属性,Proxy 可以监听到,Object.defineProperty 不能
3、数组新增修改,Proxy 可以监听到,Object.defineProperty 不能
4、若对象内部属性要全部递归代理,Proxy 可以只在调用的时候递归,而 Object.definePropery 需要一次完成所有递归,性能比 Proxy 差
5、Proxy 不兼容 IE,Object.defineProperty 不兼容 IE8 及以下