代理和反射为开发者提供了拦截并向基本操作嵌入额外行为的能力
代理
创建空代理
最简单的代理是空代理,即除了作为一个抽象的目标对象,什么也不做
代理是使用 Proxy 构造函数创建的。这个构造函数接收两个参数:目标对象和处理程序对象。
在代理对象 上执行的所有操作都会无障碍地传播到目标对象
定义捕获器
捕获器就是在处理程序对象中定义的“基本操作的 拦截器”,在代理对象上操作就会触发捕获器
捕获器参数和反射 API
所有捕获器都可以访问相应的参数,基于这些参数可以重建被捕获方法的原始行为。比如,get() 捕获器会接收到目标对象、要查询的属性和代理对象三个参数。
处理程序对象中所有可以捕获的方法都有对应的反射(Reflect)API 方法。这些方法与捕获器拦截 的方法具有相同的名称和函数签名,而且也具有与被拦截方法相同的行为。因此,使用反射 API 也可以 像下面这样定义出空代理对象:
如果真想创建一个可以捕获所有方法,然后将每个方法转发给对应反射 API 的空代理,那 么甚至不需要定义处理程序对象:
捕获器不变式
捕获处理程序的行为必须遵循“捕获器不变式” (trap invariant)。捕获器不变式因方法不同而异,但通常都会防止捕获器定义出现过于反常的行为。
比如,如果目标对象有一个不可配置且不可写的数据属性,那么在捕获器返回一个与该属性不同的 值时,会抛出 TypeError:
可撤销代理
有时候可能需要中断代理对象与目标对象之间的联系。
Proxy 也暴露了 revocable()方法,这个方法支持撤销代理对象与目标对象的关联。撤销代理的 操作是不可逆的。而且,撤销函数(revoke())是幂等的,调用多少次的结果都一样。撤销代理之后 再调用代理会抛出 TypeError。
实用反射 API
- 反射 API 与对象 API 在使用反射 API 时,要记住: (1) 反射 API 并不限于捕获处理程序; (2) 大多数反射 API 方法在 Object 类型上有对应的方法。 通常,Object 上的方法适用于通用程序,而反射方法适用于细粒度的对象控制与操作。
2.状态标记
很多反射方法返回称作“状态标记”的布尔值,表示意图执行的操作是否成功。
3. 用一等函数替代操作符
4. 安全地应用函数
代理另一个代理
代理的问题与不足
- 代理中的 this
2.代理与内部槽位
有些 ECMAScript 内置类型可 能会依赖代理无法控制的机制,结果导致在代理上调用某些方法会出错。代理捕获器与反射方法
1.get()
对应的反射 API 方法为 Reflect.get()。
作用:
2.set()
返回 false 表示失败,严格模式下会抛出 TypeError。
1. 返回值 返回 true 表示成功;返回 false 表示失败,严格模式下会抛出 TypeError。
Proxy与Reflect对应的API
get(),set()
has(),defineProperty(),getOwnPropertyDescriptor(),deleteProperty(),ownKeys(),getPrototypeOf(),setPrototypeOf(),isExtensible(),preventExtensions(),apply(),construct()代理模式
跟踪属性访问
可以监控这个对象何时在何处被访问过隐藏属性
代理的内部实现对外部代码是不可见的,因此要隐藏目标对象上的属性也轻而易举。访问这个属性,代理对象可以返回undefined,这样就实现了属性隐藏。属性验证
函数与构造函数参数验证
跟保护和验证对象属性类似,也可对函数和构造函数参数进行审查。比如,可以让函数只接收某种 类型的值:
数据绑定与可观察对象
通过代理可以把运行时中原本不相关的部分联系到一起。这样就可以实现各种模式,从而让不同的 代码互操作。
小结
代理增强操作对象的应用场景;可以创建出各种编码模式,比如(但远远不限于)跟 踪属性访问、隐藏属性、阻止修改或删除属性、函数参数验证、构造函数参数验证、数据绑定,以及可 观察对象。
与代理如影随形的反射 API,则封装了一整套与捕获器拦截的操作相对应的方法。可以把反射 API 看作一套基本操作,这些操作是绝大部分 JavaScript 对象 API 的基础。
代理可以定义包含捕获器的处理程序对象, 而这些捕获器可以拦截绝大部分 JavaScript 的基本操作和方法。在这个捕获器处理程序中,可以修改任 何基本操作的行为,当然前提是遵从捕获器不变式。