Vue3中使用Reflect避免引起this指向错误问题

187 阅读2分钟

前言

最近研究Vue3reactive实现的源码中发现,通过proxy代理后,触发getter时,返回的是Reflect.get(target, key, receiver),而不是target[key]

function createGetter(isReadonly = false, shallow = false) {
  return function get(target: Target, key: string | symbol, receiver: object) {
    ......
    const res = Reflect.get(target, key, receiver)
    return res
  }
}

地址:packages\reactivity\src\baseHandlers.ts,函数createSetter

经过探索后发现可以用一个demo来解释使用Reflect来避免引起this指向错误问题

Demo

let father = {
  _name: "father",
  age: 40,
  get name() {
    console.log(this);
    return this._name;
  },
};

let fatherProxy = new Proxy(Father, {
  get(target, prop, receiver) {
    // return target[prop];
    return Reflect.get(target, prop, receiver);
  },
});

let kid = {
  __proto__: fatherProxy,
  _name: "kid",
  age: 20,
};

console.log("kid.name", kid.name);      // kid

调试可以发现

  • return Reflect.get(target, prop, receiver),返回的是kid
  • return target[prop],返回的是father

分析

使用 Reflect.get(target, prop, receiver)

  • 最重要的是receiver这个参数,mdn上对参数receiver的解释:

image.png

  • 当调用 Reflect.get 时,它明确指定了 receiver 参数,这个参数指定了属性访问的接收者,也就是调用属性访问操作的对象。在这种情况下,调用了 kid.name,所以 receiverkid 对象。
  • Reflect.get 能够正确地获取属性,并且在代理对象中,receiver 参数的存在帮助代理知道在哪个对象上执行属性访问,因此 this 在代理中正确地指向了 kid 对象。

使用 target[prop]

  • 当使用 target[prop] 时,它并没有指定 receiver 参数,而只是在 target 对象上直接访问属性。
  • 这种方式不考虑代理对象的情况,而只关注 target 对象。在代理对象 fatherProxy 上执行 target[prop] 时,它会尝试在 fatherProxy 上查找属性,而不是在 kid 上。
  • 因此,this 在这种情况下会指向 fatherProxy,而不是 kid

结论

receiver 参数的存在帮助代理知道在哪个对象上执行属性访问,解决了上下文错乱的问题,我的理解有点像函数的bind方法,改变了this的绑定

便捷写法

通过查询JavaScript高级程序设计这本红宝书,发现proxy+Reflect会有更便捷的写法:

new Proxy(target, {
  get() {
    return Reflect.get(...arguments);
  },
});

-----------------------------------------

new Proxy(target, {
    return Reflect.get,
});

END

  • 文中有理解不到位的地方,欢迎各位的指导呀
  • 希望这篇文章可以帮助到有需要的小伙伴们,有问题可以评论或私信我呀🤞🤞