捕获器的preventExtensions
捕获器的preventExtensions()可以检测到freeze、seal、preventExtensions这三个操作,因为他们都使对象无法再扩展(添加新属性):
const a = {
name: 'xiaoming',
age: 21,
};
const pro = new Proxy(a, {
preventExtensions(target) {
console.log('preventExtensions()');
return Reflect.preventExtensions(...arguments)
}
});
Object.freeze(pro)//preventExtensions()
Object.seal(pro)//preventExtensions()
Object.preventExtensions()//preventExtensions()
捕获器的get
捕获器get
有3
个参数 (target, property, receiver)
,捕获器set
有4
个参数 (target, property, value, receiver)
- target —— 是目标对象,该对象作为第一个参数传递给 new Proxy,
- property —— 目标属性名,
- property —— 目标属性的值,
- receiver —— 如果目标属性是一个 getter 访问器属性,则 receiver 就是本次读取属性所在的 this 对象。通常,这就是 proxy 对象本身(或者,如果我们从代理继承,则是从该代理继承的对象)。在捕获器的get和set函数里打印
receiver
或者console.log(arguments);
会陷入死循环
Reflect需要注意的地方
const target = {
_name: 'xiaoming',
get name() {
return this._name;
},
};
const handler = {
_name: 'xiaohong',
get (target, property, receiver) {
//console.log(arguments);
//如果执行这个,因为会打印receiver,所以会陷入死循环
//因为console.log(arguments)中有对receiver的引用,这个操作会触发get,陷入死循环,是一种内存泄露的情况
return target[property];//**
// return Reflect.get(target, property, receiver);
},
};
const proxy = new Proxy(target, handler);
const admin = {
__proto__: proxy,
_name: 'xiaohuang',
}
console.log(admin.name);//xiaoming
console.log(proxy.name);//xiaoming
上面admin.name的值并不符合预期,如果把**
标注的地方改为return Reflect.get(...arguments);
则能打印出预期的值admin.name;//xiaohuang
。因为Reflect.get
会用第三个参数指定需要访问的this的指向。
比如:
console.log(Reflect.get(
{
_age: 1,
get age() {
return this._age;
}
},
'age',
{
_age: 2
},
));//2
这个例子会打印出2。
前边的例子中如果写为
const admin = {
__proto__: proxy,
_name: 'xiaohuang',
name: 'Admin',
}
那么admin的name属性会被覆盖为'Admin'; 但如果像下边的写法,则会出问题:
const admin = {
__proto__: proxy,
_name: 'xiaohuang',
}
admin.name = 'Admin';
或者
const admin = Object.create(proxy);
admin.name = 'Admin';
这时如果打印admin.name的值,出来的还是admin._name的值。原因在于target里只定义了name的获取set,并没有定义相应的set,被认为是无法修改的。而执行admin.name = 'Admin'会进行set的操作。如果给target设置了对应的set name则会有预期的结果。
内置对象的内部插槽
Proxy在处理Map, Set, Date, Promise等对象的方法时,会遇到一些问题,相关操作不能被拦截到,因为他们使用了“内部插槽”的方式 如Map的set,或者Date的getTime方法。和this的指向相关。
解决的思路是,在捕获器内部将这个方法从目标源上调用。因为每次调用方法时,会先进入get拦截。
get(target, prop, receiver) {
let value = Reflect.get(...arguments);
return typeof value == 'function' ? value.bind(target) : value;
}
需要注意的地方
Proxy作为构造函数,但没有.prototype属性,Proxy.prototype === undefined
;
同样作为构造函数但没有.prototype属性的,如通过bind返回的函数。箭头函数、Symbol和console.log这样的函数也没有.prototype,他们不能作为构造函数。
对象相等性测试===
不能被代理拦截