记录关于Reflect.get的困惑

目录

Reflect.get()的了解与学习,可以直接看Reflect.get() - JavaScript | MDN

不过说实话,我在看完之后,仍然理解得不甚透彻。

那么你呢?你对其的理解又如何呢?如果让你回答以下例子的输出,你能够得出正确的答案吗?(答案在最后)

问题1:

javascript 复制代码
    const obj = { bar: 1 }
    const value = Reflect.get(obj, 'bar', { bar: 2 })
    console.log('value', value)

问题2:

javascript 复制代码
    const obj = {
      get bar() {
        return 1
      }
    }
    const value = Reflect.get(obj, 'bar', { bar: 2 })
    console.log('value', value)

问题3:

javascript 复制代码
    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不会无限循环?

这个例子是这样的:

javascript 复制代码
    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会无限循环这个错误结论

  1. 读取p.bar会进入到getter,此时会return Reflect.get(target, key, receiver)
  2. 那么这时就会用receiver也就是p来替代target来读取key,也就是读取p.bar,从而又回到了第一步,从而导致无限循环。

但是,实际上这个例子是可以输出1的,并不存在无限循环,这个推导是错误的,那么实际上发生了什么呢?

以下是我理解这个例子发生的细节
obj是目标对象target,p是代理对象,在代理对象的get中,调用了Reflect.get(target, key, receiver),其中receiver其实就是代理对象p

当尝试读取p.bar时,实际上发生了以下步骤:

  1. 触发代理对象的get处理程序
  2. get处理程序中,调用了 Reflect.get(target,key,receiver) receiver就是代理对象本身
  3. 这时因为target[key]并不会触发getter,因此此时直接返回target[key]的值,也就是1。

至此流程就结束了,不存在循环的问题。

为什么这个p.bar又不会无限循环?

javascript 复制代码
    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)

同样的,我先解释一下为什么我会推导出上面这个例子无限循环的错误结论

  1. 访问p.bar,会触发getter,执行return Reflect.get(target, key, receiver)
  2. 这时target[key]的读取会触发getter,因此会使用receiver替代target,也就是会用代理对象p来替代target
  3. 这时就相当于访问p.bar,就回到了第1步,从而导致无限循环。

正确的步骤

  1. 访问p.bar,会触发getter,执行return Reflect.get(target, key, receiver)
  2. 此时target[key]会访问对象的getter,因此Relect.get会将getter中的this设置为receiver也就是代理对象p,也就相当于return p.foo
  3. p.foo同样会触发代理对象get处理程序,执行return Reflect.get(target, key, receiver),但是此时的target[key]obj.foo,它不会访问对象的getter,因此会直接返回1

至此整个流程就结束了。

结论

  1. 使用Reflect.get(target, key, receiver)时,如果target[key]会访问对象的getter时,getter中的this会被设置为receiver
  2. 使用Reflect.get(target, key, receiver)时,如果target[key]不会访问对象的getter时,那么此时会直接返回目标对象上该属性的值,也就是target[key]

最后,回到开头的问题,

问题1:

javascript 复制代码
    const obj = { bar: 1 }
    const value = Reflect.get(obj, 'bar', { bar: 2 })
    console.log('value', value) // value 1

问题2:

javascript 复制代码
    const obj = {
      get bar() {
        return 1
      }
    }
    const value = Reflect.get(obj, 'bar', { bar: 2 })
    console.log('value', value) // value 1

问题3:

javascript 复制代码
    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