Appearance
代理 / Proxy
Proxy 可以给目标对象定义一个关联的代理对象,代理对象可以作为抽象的目标对象来使用。在对目标对象的各种操作影响目标对象之前,可以在代理对象中对这些操作加以控制。
目标对象既可以直接被操作,也可以通过代理对象进行操作,直接操作目标对象则会绕过代理对象所施加的行为。
Proxy 构造函数
Proxy
构造函数接收两个参数,第一个参数为要代理的目标对象(target),第二个参数为处理程序对象(handler)。
两个参数都是必传的,否则会抛出 TypeError
错误。
Proxy.prototype
Proxy.prototype
为 undefined
,因此我们不能使用 instanceof
操作符判断一个对象是否为 Proxy
捕获器 / trap
捕获器是在处理程序对象中定义的“基本操作的拦截器”,每个处理程序对象可以包含 0 到多个捕获器。每次在代理对象上进行这些基本操作时,代理可以在这些操作传播到目标对象之前先调用捕获器函数,从而拦截并修改相对应的行为。
需要注意的是,只有在代理对象上进行这些操作,才会触发捕获器,直接在目标对象上进行操作是无法触发捕获器的。
捕获器参数
所有捕获器都可以访问相应的参数,如 get
捕获器会接收到目标对象,要查询的属性和代理对象 3 个参数。
javascript
const handler = {
get(trapTarget, property, receiver) {},
};
撤销代理
使用 Proxy 构造函数创建的代理对象无法撤销。
所幸 Proxy 提供了 revocable
静态方法,该方法接收目标对象和处理程序对象 2 个参数。返回的对象包含 proxy
和 revoke
两个属性。
调用 revoke
函数后,proxy 和 target 的关联就撤销了,并且是不可逆的。再访问代理则会抛出 TypeError
错误。
javascript
const target = {
id: 'target',
};
const handler = {
get() {
return 'hello';
},
};
const { proxy, revoke } = Proxy.revocable(target, handler);
console.log(proxy.id); // -> hello
console.log(target.id); // -> target
revoke();
console.log(proxy.id); // -> TypeError
对比 Object.defineProperty
Vue3 中舍弃了 Object.defineProperty()
方法,改用了 Proxy
实现双向绑定。以下简称 defineProperty
Proxy
应用于对象,defineProperty
应用于属性,对象新增属性时需要应用defineProperty
,而Proxy
不需要Proxy
兼容性不如defineProperty
,defineProperty
能兼容到 IE9。Proxy
监听的范围更广,如直接通过数组索引去更新数组元素。Proxy
必须通过代理对象去触发拦截与监听,defineProperty
能够通过原对象触发拦截与监听
参考资料
- 《JavaScript 高级程序设计(第四版)》