Proxy与Reflect,详解代理对象

42 阅读3分钟

什么是Proxy

定义

  • 使用 Proxy 可以创建一个代理对象
  • 它能够实现对其他对象的代理
    • 代理:对一个对象基本语义的代理,允许拦截并重新定义对一个对象的基本操作

注意点

  • Proxy 只能代理对象,无法代理非对象值

Proxy基础

基本语义

  • 对象(obj)的读取属性值、设置属性值

Proxy使用

基本使用
    • Proxy 构造函数接收两个参数
    • 第一个参数是被代理的对象
    • 第二个参数也是一个对象,这个对象是一组夹子(trap)
      • get 函数用来拦截读取操作
      • set 函数用来拦截设置操作
代理函数
  • 可以用 Proxy 来拦截函数的调用操作
复合操作
  • 调用对象下的方法就是典型的非基本操作
    • 通过 get 操作得到 obj.fn 属性
    • 调用获得到的obj.fn

Reflect基础

基础属性&方法

  • 任何在 Proxy 的拦截器中能够找到的方法,都能够在 Reflect 中找到同名函数
  • Reflect.get
    • 提供访问一个对象属性的默认行为
    • 第三个参数,指定接收者 receiver

使用场景

它与响应式数据的实现密切相关

    • 上图是实现响应式数据的最基本的代码
    • 在 get 和set 拦截函数中,都是直接使用原始对象 target 来完成对属性的读取和设置操作
    • 原始对象 target 就是上述代码中的 obj对象
  • 修改一下 obj 对象,为它添加 bar 属性
    • bar 属性是一个访问器属性,它返回了 this.foo 属性的值
  • 在 effect 副作用函数中通过代理对象 p 访问bar 属性
      • 当 effect 注册的副作用函数执行时,会读取 p.bar 属性
      • 它发现 p.bar 是一个访问器属性,因此执行 getter 函数
      • 在 getter 函数中通过 this.foo读取了 foo 属性值
      • ?认为副作用函数与属性 foo 之间也会建立联系,修改 p.foo 的值时应该能够触发响应,使得副作用函数重新执行
      • 实际并非如此,修改 p.foo 的值时副作用函数并不会执行
  • 副作用函数不执行的原因
    • bar 属性的访问器函数 getter,指向的是原始对象
        • 通过代理对象 p 访问p.bar,会触发代理对象的 get 拦截函数执行
        • 在 get 拦截函数内,通过 target[key] 返回属性值
        • target 是原始对象 obj,而 key 就是字符串 'bar',所以target[key] 相当于 obj.bar
        • 当使用 p.bar 访问 bar属性时,它的 getter 函数内的 this 指向的其实是原始对象 obj
        • 最终访问的是 obj.foo,在副作用函数内通过原始对象访问它的某个属性是不会建立响应联系
  • 解决副作用函数不执行
    • 解决代码
      • 代理对象的 get 拦截函数接收第三个参数receiver,它代表谁在读取属性
        • 使用代理对象 p 访问 bar 属性时,那么 receiver 就是p
        • 使用 Reflect.get(target, key, receiver) 代替之前的 target[key]
          • receiver就是代理对象 p
          • 访问器属性 bar 的 getter 函数内的 this 指向代理对象 p
        • this 由原始对象 obj 变成了代理对象 p,副作用函数与响应式数据之间建立了响应联系
        • 再对 p.foo 进行自增操作,会发现已经能够触发副作用函数重新执行了