proxy

55 阅读2分钟

proxy.handler

  1. proxy.handler.get 拦截对象的读取属性操作。
  2. proxy.handler.set 拦截设置属性值的操作。
  3. proxy.handler.has 可以看作是针对 in操作的钩子.
  4. proxy.handler.deleteProperty 拦截对对象属性的delete操作。
      let person = {
        name: 'lcj',
        height: 164,
        age: 31,
        hobby: 'reading',
      };
      let proxy = new Proxy(person, {
        get: (target, propKey, selfProxy) => {
          //目标对象、属性名和 proxy实例本身 (操作行为所针对的对象),最后一个参数可选。
          return propKey in target ? target[propKey] : 'Error';
        },
        set: (target, propKey, value, selfProxy) => {
          //目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。
          if (propKey === 'height') {
            if (!Number.isInteger(value)) {
              throw new TypeError(`${value} is not an int`);
            }
          }
          target[propKey] = value;
        },
        has: (target, propKey) => {//可以看作是针对 in操作的钩子.
          //用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是in运算符。has()方法可以接受两个参数,分别是目标对象、需查询的属性名。
          return target[propKey] ? true : false;
        },
        deleteProperty: (target, propKey) => {
          //用于拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除。deleteProperty() 方法接受两个参数:目标对象、目标属性
          if (propKey == 'name') {
            alert('name不可以删除');
          } else {
            delete target[propKey];
          }
          return true;
        },
      });
      proxy.sex = '男';
      proxy.height = 88;
      // console.log(person);
      // console.log('sex' in proxy); //true
      // console.log('a' in proxy); //false
      // delete proxy.name;
      // delete proxy.age;
      console.log(person); //没有了age属性

5.proxy.handler.apply 拦截函数的调用。

      let person = () => 'i am the target';
      let proxy = new Proxy(target, {
        //apply方法拦截函数的调用、call和apply操作。apply方法可以接受三个参数,分别是目标对象、目标对象的上下文对象(this)和目标对象的参数数组。
        // apply: (target, ctx, args) => 'i am the proxy',
        apply: (target, ctx, args) => {
          return Math.max(...args);//返回参数的最大值
        },
      });
      console.log(proxy(1, 2, 3));

6.proxy.handler.construct 用于拦截new操作. 为了使新的操作在生成的Proxy对象上有效,用于初始化代理的目标本身必须具有[[Construct]]内部方法(即new target必须是有效的)。

      let person = function () {};
      let proxy = new Proxy(person, {
        construct: (target, args, cxt) => {//用于拦截new命令,下面是拦截对象的写法。construct()方法可以接受三个参数: 目标对象、构造函数的参数数组、目标对象的上下文对象(this)
          //return new target(...args);//person {}
          return args.map((i) => i * 2);
        },
      });
      console.log(new proxy(1, 2, 3));//[2,4,6]

7.proxy.handler.defineProperty 拦截对对象的 Object.defineProperty() 操作。
8.proxy.handler.getOwnPropertyDescriptor 方法是Object.getOwnPropertyDescriptor() 的陷阱。
9.proxy.handler.getPrototypeOf 是一个代理方法,当读取代理对象的原型时,该方法就会被调用。
10.proxy.handler.setPrototypeOf 用来拦截Object.setPrototypeOf()
11.proxy.handler.isExtensible 拦截对对象的Object.isExtensible()
12.proxy.handler.preventExtensions 用来拦截Object.preventExtensions
13.proxy.handler.ownKeys 是一个Reflect.ownKeys()陷阱

      const handler1 = {
        defineProperty(target, propKey, descriptor) {//拦截对对象的 Object.defineProperty() 操作。
          //目标对象 属性名称 正在定义或修改的属性的描述符。
          //set方法在的时候无效
          if (propKey[0] === '_') {
            throw new Error(
              `Invalid attempt to define private "${propKey}" property`,
            );
          }
          return true;
        },
        getOwnPropertyDescriptor: (target, propKey) => {//方法是Object.getOwnPropertyDescriptor() 的陷阱。
          // console.log(`called: ${propKey}`);
          // Expected output: "called: eyeCount"
          return { configurable: true, enumerable: true, value: 5 };
        },
        getPrototypeOf(target) {//是一个代理方法,当读取代理对象的原型时,该方法就会被调用。
          return { eyeCount: 2 };
        },
        setPrototypeOf(target, prototype) {//用来拦截Object.setPrototypeOf()
          //prototype: 对象新原型或为null
          target.geneticallyModified = true;
          return false;//成功修改了返回true否则返回false
        },
        isExtensible(target) {//拦截对对象的Object.isExtensible()
          //可扩展的
          return Reflect.isExtensible(target);
        },
        preventExtensions(target) {//用来拦截Object.preventExtensions
          target.canEvolve = false;
          return Reflect.preventExtensions(target);
        },
        ownKeys(target) {//是一个Reflect.ownKeys()陷阱
          return Reflect.ownKeys(target);
        },
      };
      const monster1 = {
        eyeCount: 4,
        canEvolve: true,
        geneticallyModified: false,
        [Symbol('secret')]: 'I am scared!',
      };
      const proxy1 = new Proxy(monster1, handler1);
      // console.log((proxy1._secret = 'easily scared'));
      // Expected output: Error: Invalid attempt to define private "_secret" property
      console.log(Object.getOwnPropertyDescriptor(proxy1, 'eyeCount').value); //5
      console.log(Object.getPrototypeOf(proxy1).eyeCount); //2
      console.log(Object.isExtensible(proxy1)); //true
      console.log(monster1.canEvolve); //true
      Object.preventExtensions(proxy1);
      console.log(Object.isExtensible(proxy1)); //false
      console.log(monster1.canEvolve); //false
      //设置原型上
      console.log(Reflect.setPrototypeOf(proxy1, {})); //false
      console.log(monster1.geneticallyModified); //true
      console.log(Object.keys(proxy1)); //['eyeCount', 'canEvolve', 'geneticallyModified']

Proxy.revocable 可以用来创建一个可撤销的代理对象。

      var revocable = Proxy.revocable(
        {},
        {
          get: function (target, name) {
            return '[[' + name + ']]';
          },
        },
      );
      var proxy = revocable.proxy;
      console.log(proxy.foo); // "[[foo]]"

      revocable.revoke();

      console.log(proxy.foo); // Cannot perform 'get' on a proxy that has been revoked
      //proxy.foo = 1; // TypeError again
      //delete proxy.foo; // still TypeError
      //typeof proxy;//Error

一旦某个代理对象被撤销,它将变的几乎完全不可用,在它身上执行任何的可代理操作都会抛出TypeError异常(注意,可代理操作一共有14 种,执行这 14 种操作以外的操作不会抛出异常)。一旦被撤销,这个代理对象永远不可能恢复到原来的状态,同时和它关联的目标对象以及处理器对象将有可能被垃圾回收掉。调用撤销方法多次将不会有任何效果,当然,也不会报错。