代理
代理是目标对象的抽象。
创建空代理
代理是使用Proxy构造函数创建的。Proxy接收两个参数:目标对象和处理程序对象。缺一不可。要创建空代理,可以传一个简单的对象字面量作为处理程序对象,从而让所有操作畅通无阻地抵达目标对象。
const target = { id: 'target' }; const handler = {}; const proxy = new Proxy(target, handler); // id 属性会访问同一个值 console.log(target.id); // target console.log(proxy.id); // target // 给目标属性赋值会反映在两个对象上 // 因为两个对象访问的是同一个值 target.id = 'foo'; console.log(target.id); // foo console.log(proxy.id); // foo // 给代理属性赋值会反映在两个对象上 // 因为这个赋值会转移到目标对象 proxy.id = 'bar'; console.log(target.id); // bar console.log(proxy.id); // bar // hasOwnProperty()方法在两个地方 // 都会应用到目标对象 console.log(target.hasOwnProperty('id')); // true console.log(proxy.hasOwnProperty('id')); // true // Proxy.prototype 是 undefined // 因此不能使用 instanceof 操作符 console.log(target instanceof Proxy); // TypeError: Function has non-object prototype 'undefined' in instanceof check console.log(proxy instanceof Proxy); // TypeError: Function has non-object prototype 'undefined' in instanceof check // 严格相等可以用来区分代理和目标 console.log(target === proxy); // false
定义捕获器
捕获器就是在处理程序对象中定义的“基本操作的拦截器”。每个处理程序对象可以包含零个或多个捕获器,每个捕获器都对应一种基本操作,可以直接或间接在代理对象上调用。每次在代理对象上调用这些基本操作时,代理可以在这些操作传播到目标对象之前先调用捕获器函数,从而拦截并修改相应的行为。
const target = { foo: 'bar' }; const handler = { // 捕获器在处理程序对象中以方法名为键 get() { return 'handler override'; } }; const proxy = new Proxy(target, handler); console.log(target.foo); // bar console.log(proxy.foo); // handler override console.log(target['foo']); // bar console.log(proxy['foo']); // handler override console.log(Object.create(target)['foo']); // bar console.log(Object.create(proxy)['foo']); // handler override
捕获器不变式
使用捕获器几乎可以改变所有基本方法的行为,但也不是没有限制。根据 ECMAScript 规范,每个捕获的方法都知道目标对象上下文、捕获函数签名,而捕获处理程序的行为必须遵循“捕获器不变式” (trap invariant)。捕获器不变式因方法不同而异,但通常都会防止捕获器定义出现过于反常的行为。可撤销代理
revocable()方法支持撤销代理对象与目标对象的关联,撤销代理的操作是不可逆的。撤销函数是幂等的,调用多少次的结果都一样。
const target = { foo: 'bar' }; const handler