JavaScript学习笔记(十八)代理基础
- 代理类似C++指针,因为它可以用作目标对象的替身,但又完全独立于目标对象;
- 目标对象既可以直接被操作,也可以通过代理来操作。
- 默认情况下,在代理对象上执行的所有操作都会无障碍地传播到目标对象。
- 在任何可以使用目标对象的地方,都可以通过同样的方式来使用与之关联的代理对象
- 直接操作会绕过代理施予的行为。
空代理
- 除了作为一个抽象的目标对象,什么也不做
- 代理是使用Proxy构造函数创建的,构造函数接收两个参数:目标对象和处理程序对象。
- 缺少其中任何一个参数都会抛出TypeError;
- 创建空代理,可以传一个简单的对象字面量作为处理程序对象。
1 | const target = { |
定义捕获器
- 捕获器就是在处理程序对象中定义的“基本操作的拦截器”。
- 每个处理程序对象可以包含零个或多个捕获器,每个捕获器都对应一种基本操作,可以直接或间接在代理对象上调用。
- 每次在代理对象上调用这些基本操作时,代理可以在这些操作传播到目标对象之前先调用捕获器函数,从而拦截并修改相应的行为。
- 只有在代理对象上执行操作才会触发捕获器。
1 | const target = { |
捕获器参数和反射API
- 所有捕获器都可以访问相应的参数,基于这些参数可以重建被捕获方法的原始行为。
1 | const target = { |
- 所有捕获器都可以基于自己的参数重建原始操作。
- 通过调用全局Reflect对象上(封装了原始行为)的同名方法来轻松重建原始操作。
1 | const target = { |
- 事实上,如果真想创建一个可以捕获所有方法,然后将每个方法转发给对应反射API的突代理那么甚至不需要定义处理程序对象:
1 | const target = { |
捕获器不变式
- 每个捕获的方法都知道目标对象上下文、捕获函数签名。
- 捕获处理程序的行为必须遵循“捕获器不变式”。
- 捕获器不变式因方法不同而异,但通常都会防止捕获器定义出现过于反常的行为
- 如果目标对象有一个不可配置且不可写的数据属性,那么在捕获器返回一个与该属性不同的值时,会抛出TypeError:
1 | const target = {}; |
可撤销代理
- 使用new Proxy()创建的普通代理,这种联系会在代理对象的生命周期内一直持续存在。
- Proxy的revocable()方法,这个方法支持撇销代理对象与目标对象的关联。
- 撤销代理的操作是不可逆的;
- 撤销函数(revoke())是幂等的,调用多少次的结果都一样;
- 撤销代理之后再调用代理会抛出TypeError。
- 撤销函数和代理对象是在实例化时同时生成的。
1 | const target = { |
实用反射API
反射API与对象API
- 反射API并不限于捕获处理程序。
- 大多数反射API方法在Object类型上有对应的方法。
- Object上的方法适用于通用程序,而反射方法适用于细粒度的对象控制与操作。
状态标记
- 状态标记是指:反射方法返回的布尔值;表示意图执行的操作是否成功。
以下反射方法会提供状态标记:
- Reflect.defineProperty ()
- Reflect.preventExtensions ()
- Reflect.setPrototypeof ()
- Reflect.set ()
- Reflect.deleteProperty ()
1 | // 初始代码 |
用一等函数代替操作符
安全地应用函数
- 通过apply方法调用函数时,被调用的函数可能也定义了自己的apply属性,为绕过这个问题我们可以采取以下两种方法来避免:
- Function.prototype.apply.call(myFunc, thisVal, argumentList);
- Reflect.apply(myFunc, thisVal, argumentsList);
代理另一个代理
1 | const target = { |
代理存在的问题与不足
- 代理中的this;
- 代理与内部槽位;
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 姚永坤的小窝!
评论