JS - Proxy Proxy Capture Principle & Proxy Revocation

ES6's new proxy (Proxy) and reflection (Reflect) provide developers with the ability to intercept and embed additional behaviors into basic operations. Specifically, an associated proxy object can be defined for the target object, and this proxy object can be used as The abstract target object is used, that is, before performing various operations on the target object, these operations can be controlled in the proxy object

⚠️: Since the proxy is a new basic language capability, many translators cannot convert the proxy behavior into the previous ECMAScript code, and the proxy behavior is actually irreplaceable. In other words, the proxy can only be 100% support their use on the platform

mind Mapping

Agent basis

The agent is an abstraction of the target object, which can be used as a substitute for the target object. It is completely independent of the target object. The target object can be operated directly or through a proxy, that is, any operation performed by the proxy object silly girl will actually be applied to the target object, the only perceivable difference is that the proxy object is operated in the code; but the proxy object and the target object do not access the same address

const target = {
  id: "id15",
};

const handlder = {};

const proxy = new Proxy(target, handlder);
// 代理对象和目标对象访问的属性是同名的时候,本质访问的就是同一个值,
// 给目标对象 或者代理对象 属性赋值也会反映到两个对象上
log(proxy.id === target.id); // log: true

target.id = "id_15";
log(proxy.id); // log: id_15
log(target.id); // log: id_15

proxy.id = "pro_id";
log(proxy.id); // log: pro_id
log(target.id); // log: pro_id

// Proxy.rototype 是 undefiend,所以不能使用 instanceof 操作符
log(target instanceof Proxy); // TypeError: Function has non-object prototype 'undefined' in instanceof check

catcher

The main purpose of using a proxy is to customize the trap. A catcher is an interceptor defined in a handler object. For each handler object, zero or more catchers can be defined. Each catcher corresponds to a basic operation, which can be called directly or indirectly on the proxy object. Every time these basic operations are called on the proxy object, the proxy can intercept and modify the corresponding behavior by calling the catcher function before these operations are propagated to the target object.

All catchers can reconstruct the original operation based on their own parameters, which can be easily reconstructed by calling the method of the same name on the global Reflect object (encapsulating the original behavior), that is, all methods that can be captured in the handler object have corresponding Reflect API methods that have the same name and function signature as the method intercepted by the capturer, and also behave the same as the intercepted method

const proxy = new Proxy(
  { name: "jakequc" },
  {
    // 捕获器在处理程序对象中以方法名为键
    // proxy.property / proxy[property] 等获取属性的时候会出发 get 捕获器
    get(target, key, receiver) {
      // target 是代理的目标对象,key 获取时的 键(属性),receiver 是代理对象
      // log(target, key, receiver);
      return target[key] + "__";
    },
  }
);

log(proxy.name); // log: jakequc__

const reproxy = new Proxy(
  {
    age: 23,
  },
  {
    // 甚至可以写成 get: Reflect.get
    get() {
      // 每一个 Proxy 中的拦截器名字 在 Reflect 中都存在,因此可以在 Proxy 拦截器中使用 Reflect
      return Reflect.get(...arguments);
    },
  }
);

log(reproxy.age); // log: 23

If you want to create a proxy that can have all capture methods and then forward each method to the corresponding Reflect API, you can pass the second parameter of Proxy directly as Reflect

const allReflectPro = new Proxy(
  {
    name: "kj",
  },
  Reflect
);

log(allReflectPro.name); // log: kj

catcher invariant

使用捕获器几乎可以改变所有基本方法的行为,但是也有限制;虽然每个捕获的方法都知道目标对象上下文、捕获函数签名,而捕获处理程序的行为必须遵循“捕获器不变式”,防止捕获器定义出现过于反常行为;如目标对象有一个不可配置且不可写的数据属性,那么捕获器返回一个与该属性不同的值时,会抛出 TypeError

const target = {};
Object.defineProperty(target, "const_key", {
  configurable: false, // 不可删除
  writable: false, // 不可写入
  value: "love", // const_key 的 值为 love 字符串
});

const proxy = new Proxy(target, {
  get() {
    // 这里违背了 目标对象 const_key 不可写入(更改)的规则,因此在获取对应的值时会触发 TypeError
    return "update_love";
  },
});

log(proxy.const_key); // TypeError: 'get' on proxy: property 'const_key' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value (expected 'love' but got 'update_love')

可撤销代理

使用 new Proxy(target, handler) 创建普通代理来说,这种联系会在代理对象的生命周期内一直持续存在,Proxy.revocable(target,handler) 这个方法返回 revoke 方法可以撤销代理操作,值得注意的是,撤销代理的操作是不可逆的且 revoke 方法是幂等的(即调用多少次结果都一样),撤销代理之后再调用代理会抛出 TypeError;

// 代理对象和撤销代理函数在调用 revocable 在实例化同时生成
const { proxy, revoke } = Proxy.revocable(
  {
    name: "kj",
  },
  Reflect
);

log(proxy.name); // log: kj

revoke();

// 撤销代理之后,再使用代理就会报 TypeError 错误
log(proxy.name); // TypeError: Cannot perform 'get' on a proxy that has been revoked

Guess you like

Origin juejin.im/post/7253437782333538362