为什么需要receiver
最近在看回阮一峰老师的ECMAScript6入门,在Proxy一章中提到的13种拦截方法,其中get 和 set 都有一个receiver参数,原文的解释是:原始的读/赋值操作所在的那个对象,并且有两段代码示例:
ini
const proxy = new Proxy({}, {
get: function(target, key, receiver) {
return receiver;
}
});
proxy.getReceiver === proxy // true
这一段就是解释了receiver
指向原始操作对象。但是target
不也是跟receiver
一个指向吗,那为什么还要receiver
呢,接着看第二段示例代码:
ini
const proxy = new Proxy({}, {
get: function(target, key, receiver) {
return receiver;
}
});
const d = Object.create(proxy);
d.a === d // true
这里用了一个Proxy
去Object.create
创建d
对象,那么在读取d
对象的时候,由于没有自身属性,就会去原型上找,找到proxy
,然后被get
拦截,返回receiver
,这里的receiver
却是指向了d
对象。
那么很明显,在这类型问题上,指向就是receiver
所要解决的问题了
实际问题
既然是指向问题,那么this就是我们始终绕不开的话题,我们就用this指向来举个例子。
js
let user = {
_name: 'user',
get name() {
return this._name;
}
};
let userProxy = new Proxy(user, {
get(target, propKey, receiver) {
return target[propKey];
// return Reflect.get(target, propKey);
}
});
let admin = Object.create(userProxy);
admin._name = 'admin';
console.log(admin.name); // user
发现问题
这里的user
用了_name
表示name
是私有的,并且使用了get
函数来获取,然后Proxy
拦截读取操作,返回值直接使用了对象取值的方式。然后用声明admin
去继承userProxy
,并且给admin
自己取名为admin
。
然而在读取admin.name
时却发现名字是user
,这里非常简单,因为Proxy.get
中的target
是user
,所以返回的是user._name
。
同样的使用Reflect
对象去还原原生行为,但不传入receiver
,admin.name
依然是user
。
解决问题
当我们传入receiver
js
let user = {
_name: 'user',
get name() {
return this._name;
}
};
let userProxy = new Proxy(user, {
get(target, propKey, receiver) {
return Reflect.get(target, propKey, receiver);
}
});
let admin = Object.create(userProxy);
admin._name = 'admin';
console.log(admin.name); // admin
admin.name
就变成了admin
,这才是我们想要的结果。
为什么要用Reflect对象
这里还有一点,我们直接target[propKey]
的方式能用上receiver
吗?
对象的get
函数,Proxy.get
需要返回一个修改了this
指向的函数,那可以返回target[propKey].bind(receiver)
吗?
答案是不可以的,对象的get
函数是比较特殊的存在,表现形式是函数,但他其实是个属性,当你访问target[propKey]
的时候,他已经是user
字符串了,自然的在它的原型上是找不到bind
方法的。