深入理解ES6-12.代理(Proxy)和反射(Reflection)

212 阅读2分钟

代理陷阱

JS引擎内部有很多底层对象操作,比如读取一个属性值或者写入一个属性等操作,这些操作我们很难去改变的。所以为了让我们有机会改变这些默认的操作,JS对这些操作都设置了代理陷阱

完整的代理陷阱可以参照 MDN

代理陷阱覆写的特性默认特性
get读取一个属性值Reflect.get()
set写入一个属性Reflect.set()
hasin操作符Reflect.has()
deletePropertydelete操作符Reflect.deleteProperty()
getPrototypeOfObject.getPrototypeOf()Reflect.getPrototypeOf()
setPrototypeOfObject.setPrototypeOf()Reflect.setPrototypeOf()
isExtensibleObject.isExtensible()Reflect.isExtensible()
preventExtensionsObject.preventExtensions()Reflect.preventExtensions()
getOwnPropertyDescriptorObject.getOwnPropertyDescriptor()Reflect.getOwnPropertyDescriptor()
definePropertyObject.defineProperty()Reflect.defineProperty()
ownKeysObject.keys()/Object.getOwnPropertyNames()/Object.getOwnPropertySymbols()Reflect.ownKeys()
apply调用一个函数Reflect.apply()
construct用new调用一个函数Reflect.construct()

Proxy

使用 Proxy 可以拦截对象的代理陷阱,从而改变对象的默认操作。

创建

new Proxy() 构造函数接收两个参数,目标对象代理陷阱处理对象

let target = {};
let proxy = new Proxy( target, {});

这里的 proxy 不会存储任何属性,在这个 proxy 上进行的任何操作都会被转发给目标对象。

Reflection

每个代理陷阱对应一个命名和参数都相同的 Reflect 方法,他可以替代这些对象的默认操作。也就是说 Reflection 是默认就有的,不需要我们手动创建,可以参考代理陷阱的表格。

Demo

对于这些概念看起来挺复杂的,但是我可以可以通过一个案例,对 set 陷阱添加验证来说明,代理和反射的用法。

set 陷阱接收4个参数:

  1. trapTarget,用于接收属性的对象
  2. key,要写入的属性键
  3. value,被写入的值
  4. receiver 操作发生的对象
let target = { name: "target" };
let proxy = new Proxy( target, {
  set(trapTarget, key, value, receiver) {
      // 忽略不希望受到影响的已有属性
      if (!trapTarget.hasOwnProperty(key)) {
          if (isNaN(value)) {
              throw new TypeError('属性必须是数字');
          }
      }
      // 通过反射执行默认的set操作
      return Reflect.set(trapTarget, key, value, receiver);
  }
});

经过这样的代理,我们给 target 对象添加了 set 陷阱处理,当再往 target 对象上写入一个属性的时候,会先判断是否为数字,是的话执行写入对象的默认操作,不是数字抛出错误。

proxy.count = 1;
console.log(proxy.count); // 1
console.log(target.count); // 1