代理和反射为开发者提供了拦截并向基本操作嵌入额外行为的能力

代理

创建空代理

最简单的代理是空代理,即除了作为一个抽象的目标对象,什么也不做
代理是使用 Proxy 构造函数创建的。这个构造函数接收两个参数:目标对象和处理程序对象。
在代理对象 上执行的所有操作都会无障碍地传播到目标对象
image.pngimage.png

定义捕获器

捕获器就是在处理程序对象中定义的“基本操作的 拦截器”,在代理对象上操作就会触发捕获器
image.png

捕获器参数和反射 API

所有捕获器都可以访问相应的参数,基于这些参数可以重建被捕获方法的原始行为。比如,get() 捕获器会接收到目标对象、要查询的属性和代理对象三个参数。
image.pngimage.png
处理程序对象中所有可以捕获的方法都有对应的反射(Reflect)API 方法。这些方法与捕获器拦截 的方法具有相同的名称和函数签名,而且也具有与被拦截方法相同的行为。因此,使用反射 API 也可以 像下面这样定义出空代理对象:
image.png
如果真想创建一个可以捕获所有方法,然后将每个方法转发给对应反射 API 的空代理,那 么甚至不需要定义处理程序对象:
image.png

捕获器不变式

捕获处理程序的行为必须遵循“捕获器不变式” (trap invariant)。捕获器不变式因方法不同而异,但通常都会防止捕获器定义出现过于反常的行为。
比如,如果目标对象有一个不可配置且不可写的数据属性,那么在捕获器返回一个与该属性不同的 值时,会抛出 TypeError:
image.png

可撤销代理

有时候可能需要中断代理对象与目标对象之间的联系。
Proxy 也暴露了 revocable()方法,这个方法支持撤销代理对象与目标对象的关联。撤销代理的 操作是不可逆的。而且,撤销函数(revoke())是幂等的,调用多少次的结果都一样。撤销代理之后 再调用代理会抛出 TypeError。

image.png

实用反射 API

  1. 反射 API 与对象 API 在使用反射 API 时,要记住: (1) 反射 API 并不限于捕获处理程序; (2) 大多数反射 API 方法在 Object 类型上有对应的方法。 通常,Object 上的方法适用于通用程序,而反射方法适用于细粒度的对象控制与操作。
    2.状态标记
    很多反射方法返回称作“状态标记”的布尔值,表示意图执行的操作是否成功。image.png
    3. 用一等函数替代操作符
    image.png
    4. 安全地应用函数
    image.png

    代理另一个代理image.png

    代理的问题与不足

  2. 代理中的 this
    image.png
    2.代理与内部槽位
    有些 ECMAScript 内置类型可 能会依赖代理无法控制的机制,结果导致在代理上调用某些方法会出错。image.png

    代理捕获器与反射方法

    1.get()

    对应的反射 API 方法为 Reflect.get()。
    作用:
    image.png

    2.set()

    返回 false 表示失败,严格模式下会抛出 TypeError。
    1. 返回值 返回 true 表示成功;返回 false 表示失败,严格模式下会抛出 TypeError。
    image.png

    Proxy与Reflect对应的API

    get(),set()
    has(),defineProperty(),getOwnPropertyDescriptor(),deleteProperty(),ownKeys(),getPrototypeOf(),setPrototypeOf(),isExtensible(),preventExtensions(),apply(),construct()

    代理模式

    跟踪属性访问

    可以监控这个对象何时在何处被访问过image.png

    隐藏属性

    代理的内部实现对外部代码是不可见的,因此要隐藏目标对象上的属性也轻而易举。访问这个属性,代理对象可以返回undefined,这样就实现了属性隐藏。

    属性验证image.png

    函数与构造函数参数验证

    跟保护和验证对象属性类似,也可对函数和构造函数参数进行审查。比如,可以让函数只接收某种 类型的值:
    image.pngimage.png

    数据绑定与可观察对象

    通过代理可以把运行时中原本不相关的部分联系到一起。这样就可以实现各种模式,从而让不同的 代码互操作。
    image.png
    image.png

    小结

    代理增强操作对象的应用场景;可以创建出各种编码模式,比如(但远远不限于)跟 踪属性访问、隐藏属性、阻止修改或删除属性、函数参数验证、构造函数参数验证、数据绑定,以及可 观察对象。
    与代理如影随形的反射 API,则封装了一整套与捕获器拦截的操作相对应的方法。可以把反射 API 看作一套基本操作,这些操作是绝大部分 JavaScript 对象 API 的基础。
    代理可以定义包含捕获器的处理程序对象, 而这些捕获器可以拦截绝大部分 JavaScript 的基本操作和方法。在这个捕获器处理程序中,可以修改任 何基本操作的行为,当然前提是遵从捕获器不变式