目录
- 为什么p.bar不会无限循环?
- 为什么这个p.bar又不会无限循环?
- 结论
Reflect.get()的了解与学习,可以直接看Reflect.get() - JavaScript | MDN。
不过说实话,我在看完之后,仍然理解得不甚透彻。
那么你呢?你对其的理解又如何呢?如果让你回答以下例子的输出,你能够得出正确的答案吗?(答案在最后)
问题1:
const obj = { bar: 1 }const value = Reflect.get(obj, 'bar', { bar: 2 })console.log('value', value)
问题2:
const obj = {get bar() {return 1}}const value = Reflect.get(obj, 'bar', { bar: 2 })console.log('value', value)
问题3:
const obj = {get bar() {return this.foo},foo:1}const value = Reflect.get(obj, 'bar', { bar: 2,foo:3 })console.log('value', value)
我的困惑,其实是在阅读《Vue.js设计与实现》中提到Reflect.get的代码时产生的。总的来说,我的困惑可以用以下的两个例子来表达:
为什么p.bar不会无限循环?
这个例子是这样的:
const obj = { bar: 1 };const p = new Proxy(obj, {get(target, key, receiver) {return Reflect.get(target, key, receiver);}});console.log(p.bar)
先说说为什么我会得到p.bar会无限循环这个错误结论:
- 读取
p.bar会进入到getter,此时会return Reflect.get(target, key, receiver) - 那么这时就会用
receiver也就是p来替代target来读取key,也就是读取p.bar,从而又回到了第一步,从而导致无限循环。
但是,实际上这个例子是可以输出1的,并不存在无限循环,这个推导是错误的,那么实际上发生了什么呢?
以下是我理解这个例子发生的细节
obj是目标对象target,p是代理对象,在代理对象的get中,调用了Reflect.get(target, key, receiver),其中receiver其实就是代理对象p
当尝试读取p.bar时,实际上发生了以下步骤:
- 触发代理对象的
get处理程序 - 在
get处理程序中,调用了Reflect.get(target,key,receiver),receiver就是代理对象本身 - 这时因为
target[key]并不会触发getter,因此此时直接返回target[key]的值,也就是1。
至此流程就结束了,不存在循环的问题。
为什么这个p.bar又不会无限循环?
const obj = {foo: 1,get bar() {return this.foo}}const p = new Proxy(obj, {get(target, key, receiver) {return Reflect.get(target, key, receiver)}})console.log(p.bar)
同样的,我先解释一下为什么我会推导出上面这个例子无限循环的错误结论:
- 访问
p.bar,会触发getter,执行return Reflect.get(target, key, receiver) - 这时
target[key]的读取会触发getter,因此会使用receiver替代target,也就是会用代理对象p来替代target - 这时就相当于访问
p.bar,就回到了第1步,从而导致无限循环。
正确的步骤:
- 访问
p.bar,会触发getter,执行return Reflect.get(target, key, receiver) - 此时
target[key]会访问对象的getter,因此Relect.get会将getter中的this设置为receiver也就是代理对象p,也就相当于return p.foo p.foo同样会触发代理对象get处理程序,执行return Reflect.get(target, key, receiver),但是此时的target[key]是obj.foo,它不会访问对象的getter,因此会直接返回1
至此整个流程就结束了。
结论
- 使用
Reflect.get(target, key, receiver)时,如果target[key]会访问对象的getter时,getter中的this会被设置为receiver - 使用
Reflect.get(target, key, receiver)时,如果target[key]不会访问对象的getter时,那么此时会直接返回目标对象上该属性的值,也就是target[key]
最后,回到开头的问题,
问题1:
const obj = { bar: 1 }const value = Reflect.get(obj, 'bar', { bar: 2 })console.log('value', value) // value 1
问题2:
const obj = {get bar() {return 1}}const value = Reflect.get(obj, 'bar', { bar: 2 })console.log('value', value) // value 1
问题3:
const obj = {get bar() {return this.foo},foo:1}const value = Reflect.get(obj, 'bar', { bar: 2,foo:3 })console.log('value', value) // value 3