查看官方文档的时候,有一部分不太能理解
参考了
MDN的Proxy以及Reflect之后,get到这个点,以下是我的个人见解:
---Proxy的handle.get(target, property[, receiver])---
let obj = { a:1 }
var p = new Proxy(obj, {
get: function(target, property, receiver) {
console.log(target) // {a:1}
console.log(property) // 'a'
return receiver
}
});
console.log(p.a) // Proxy {a:1}
其中三个参数:target、property、receiver
1、target:顾名思义:源目标对象
2、property:属性名,例子中为'a'
3、receiver:是一个Proxy类型的对象或者继承Proxy的对象(注意,这个对象区别于target目标对象)或者是Reflect.get第三个参数指定的值
如何理解这里的继承Proxy的对象,看下面的例子:
let obj = { a:1 }
var p = new Proxy(obj, {
get: function(target, property, receiver) {
console.log(target) // {a:1}
console.log(property) // 'a'
return receiver
}
});
let p2 = Object.create(p) //以p为原型,创造一个对象p2
console.log(p.a) // Proxy {a:1} 1/Proxy类型的对象
console.log(p2.a) // {} 2/继承Proxy的对象,即p2这个空对象
Reflect.get(p,'a','asd') // 'asd' 3/Reflect.get第三个参数指定的值
其中有个Reflect.get,后面会说到。
---Reflect.get(target, property[, receiver])---
1、背景: Reflect是一个挺有用的方法,具体有哪些api可以参照MDN,可以打开新世界大门~~
2、方法解释: 这里的get方法,其实就是对象取值的一种装逼写法。
let obj = { a:1 }
Reflect.get(obj,'a') // 1 其实就是 obj.a
tips: 但是除了装逼的取值作用,还有一个挺有用的参数,那就是receiver,下面详细讲解:
3、何谓receiver
MDN给出的解释是:
如果
target对象中指定了getter,receiver则为getter调用时的this值。
这样说有点抽象,直接上代码(解释在注释中):
let obj = {
a: 2,
get a(){ // `target`对象中指定了`getter`
return this.b
},
c:'c'
}
console.log(obj.a) // undefined
console.log(Reflect.get(obj, 'a')) // undefined 因为Reflect.get(obj,'a')===obj.a
console.log(Reflect.get(obj, 'a', {b: 'hhh'})) // 'hhh' 解释`receiver`则为`getter`调用时的`this`值
// 等价于obj.a,但是因为a具有getter,于是走getter函数,取的是this.b所以为第三个参数的'hhh'
console.log(Reflect.get(obj,'c','hhh')) // 'c' 并不影响参数1[参数2],即obj['c']的取值,影响的为this指向
// 等价于obj.c
---前两者的receiver结合---
前面提到:Proxy.get的第三个参数会是Reflect.get第三个参数指定的值,即:
let obj = { a:1 }
var p = new Proxy(obj, {
get: function(target, property, receiver) {
return receiver
}
});
console.log(Reflect.get(p, 'a', 'hhh')) // 'hhh'
所以对于上面MDN对Reflect.get的receiver参数的解释,我认为还需要加上一个,即可以影响Proxy的handler.get中的receiver的取值
问题来了,要是原对象有getter,代理又有getter会产生出哪些火花呢
答:各走各的路线,注意区分代理对象和源对象,已经为两个对象了,唯一的联系在回调的target参数里。
直接上代码:
let obj = {
get a(){
return this.b
},
b: 'b',
c: 'c'
}
var p = new Proxy(obj, {
get: function(target, property, receiver) {
return receiver
}
})
console.log(p.a) // Proxy {b: 'b', c: 'c'}
console.log(obj.a) // 'b'
当代理中,使用了target相关的逻辑,则有些许变化了:
let obj = {
get a(){
return this.b
},
b: 'b',
c: 'c'
}
var p = new Proxy(obj, {
get: function(target, property, receiver) {
return receiver
}
})
let p2 = new Proxy(obj, {
get: function(...args) {
return Reflect.get(...args) // 等价于使用了target[property],与源对象obj产生了联系
}
})
console.log(p2.a) // 'b'
注意其中的Reflect.get(...args),等价于使用了target[property],于是与源对象obj产生了联系。
分析下运行流程:
1、console.log(p2.a)
2、进入代理对象p2的get函数中
3、执行Reflect.get(target,property,receiver),其中target为obj,property为'a',receiver为p2这个代理对象
4、第3步等价于执行obj['a'],并且将obj的a的getter方法中的this置为p2这个代理对象
5、进入obj的a属性的getter方法
6、执行return this.b,由于第3、4步中重置了this为p2代理对象
7、等价于执行 return p2.b
8、继续进入p2的get方法
9、执行Reflect.get(target,property,receiver),其中target为obj,property为b,receiver为p2这个代理对象
10、第9步等价于执行obj['b'],并且将obj的b的getter方法中的this置为p2这个代理对象
11、obj中的b属性并没有getter拦截,所以直接取出值'b',返回出来
---再次回到开头提到的vue3部分---
根据上面的执行流程,可以总结出,通过
const dinner = {
meal: 'tacos'
}
const handler = {
get(target, property, receiver) {
return Reflect.get(...arguments)
}
}
const proxy = new Proxy(dinner, handler)
console.log(proxy.meal)
return Reflect.get(...arguments)写法,实现让所有源对象中的拦截的this,指向vue给响应数据创建的代理对象,进而实现将任何方法都绑定到这个Proxy,而不是目标对象,统一进行一些响应式捕获处理的操作