ES6+ Proxy与Reflect使用

ES6+ Proxy与 Reflect 使用

说明

Proxy 译为 “代理器”,用于修改某些操作的默认行为,可以认为是在目标对象|函数之前架设一层“拦截”,外接的访问,都必须通过这层拦截,这层拦截中可以对外接的访问进行过滤和改写

1. 基本语法

  • target 指被代理的目标对象或函数
  • handler 定制的用来拦截外界操作行为的对象
  • traps 对应外界操作的拦截方法
  • new Proxy() 用来创建一个 proxy 实例
const handler = {
    [traps](...args) {
        ...
    }
};
const obj = new Proxy(target, handler)

2. 支持的拦截操作

  1. get(target, key, receiver) 拦截读取操作
  2. set(target, key, value, receiver) 拦截赋值操作
  3. has(target, key) 拦截 in 操作符操作(for in 循环不算)
  4. deleteProperty(target, key) 拦截 delete 操作符操作
  5. ownKeys(target) 拦截 Object.getOwnPropertyNames(proxy) | Object.getOwnPropertySymbols(proxy) | Object.keys(proxy) 这三个获取键名的操作
  6. getOwnPropertyDescriptor(target, key) 拦截获取属性描述符的操作 Object.getOwnPropertyDescriptor(proxy, key)
  7. defineProperty(target, key, description) 拦截定义属性操作符的操作 Object.defineProperty(proxy, key, description) | Object.defineProperties(proxy, descriptions)
  8. preventExtensions(target) 拦截将对象变为不可扩展的操作 Object.preventExtensions(proxy)
  9. isExtensible(target) 拦截判断对象是否是可扩展的操作 Object.isExtensible(proxy)
  10. getPrototypeOf(target) 拦截返回原型对象的操作,Object.getPrototypeOf(proxy)
  11. apply(target, context, args) 拦截 target 为函数时,调用函数的操作 包括:fn() | fn.call() | fn.apply()
  12. construct(target, args) 拦截以 Proxy 实例为构造函数调用的操作 new Fn()
const def = {
   _def: 'def obj'
};

const proxy = new Proxy(def, {
   // 拦截取值操作
   get(target, key) {
      // get拦截需要有返回值
      return key in target ? target[key] : `target: ${JSON.stringify(target)} Not found key: ${key}`;
   },
   // 拦截赋值操作
   set(target, key, value) {
      if (key === 'age') {
         return console.warn('age is a secret');
      }

      target[key] = value;
   },
   // 拦截 in 操作符操作
   has(target, key) {
      if(key[0] === '_') {
         return false;
      }

      return key in target;
   },
   // 拦截 delete 操作,抛出错误或者返回 false 都无法删除
   deleteProperty(target, key) {
      if (key[0] === '_') {
         console.warn(`Cant delete prop: ${key}`);
         return false;
      }
      console.log('delete', key);
      return true;
   },
   // 拦截 Object.keys() 等获取属性名操作, 返回数组的每一项如果 proxy 中不存在这个属性则会被过滤掉
   ownKeys(target) {
      // proxy 中没有 a 属性所以即使写在返回值中也不会被返回
      return ['a', '_def'];
   }
});

// 取值测试
console.log(proxy.color); // target: {"_def":"def obj"} Not found key: color

// 赋值测试
proxy.age = 90; // age is a secret
proxy.name = '小白';
console.log(JSON.stringify(proxy)); // {"_def":"def obj","name":"小白"}

// in 测试
console.log('_def' in proxy); // false

for (key in proxy) {
   console.log(key); // _def name  has 拦截对 for-in 不生效
}

// delete 测试
delete proxy._def; // Can't delete prop: _def

// Object.keys() 测试
console.log(Object.keys(proxy)); // ["_def"]

/* =========== ============ ============ ============ =========== */

const def = function (a, b) {
   return a * b;
};

const Def = new Proxy(def, {
   // 拦截函数调用
   apply(target, context, args) {
      console.log(context, args, target(...args));
      return target(...args);
   },
   // 拦截构造函数调用
   construct(target, args) {
      if (args.length < 2) {
         console.warn('The arguments is not enough')
         return {value: null};
      }
      return {value: target(...args)};
   }
});

// 测试函数调用

Def(90, 20); // undefined [90, 20] 1800
const ctx = {name: 'ctx'};

Def.call(ctx, 90, 20, 30); // {name: "ctx"} [90, 20, 30] 1800

// 测试构造函数调用
new Def(79); // The arguments is not enough
console.log(new Def(89, 27)); // {value: 2403}

3. Reflect 反射

Reflect 对象的方法与 Proxy 对象的方法一一对应,这样无论在 Proxy 上怎么修改默认行为,都可以通过调用 Reflect 方法来完成默认行为

const def = {
   _def: 'def'
};

const proxy = new Proxy(def, {
   get(target, key) {
      console.log(target, target[key]);
      return Reflect.get(target, key); // 触发默认操作
   }
});

console.log(proxy._def); // def

猜你喜欢

转载自blog.csdn.net/mjzhang1993/article/details/79805614