ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

ES6 Proxy

2021-07-16 14:32:32  阅读:181  来源: 互联网

标签:ES6 target 对象 propKey Proxy 拦截 proxy


Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。

ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。

var proxy = new Proxy(target, handler);

target:

    表示所要拦截的目标对象(必输)

handler:

    handler参数也是一个对象,用来定制拦截行为(必输)

Proxy 实例也可以作为其他对象的原型对象

var proxy = new Proxy({}, {
  get: function(target, propKey) {
    return 35;
  }
});

let obj = Object.create(proxy);

同一个拦截器函数,可以设置拦截多个操作

 

    1. Proxy 支持的拦截操作一共13种(handler中的有效属性共13种)
          • get(target, propKey, receiver):拦截对象属性的读取,比如proxy.fooproxy['foo']
            • 参数:
              • target:目标对象
              • propKey:属性名
              • receiver:proxy 实例本身(严格地说,是操作行为所针对的对象)
            • var person = {
                name: "张三"
              };
              
              var proxy = new Proxy(person, {
                get: function(target, propKey) {
                  if (propKey in target) {
                    return target[propKey];
                  } else {
                    throw new ReferenceError("Prop name \"" + propKey + "\" does not exist.");
                  }
                }
              });
              
              proxy.name // "张三"
              proxy.age // 抛出一个错误

              在 CodeSandBox 上尝试

            • get方法可以继承
              • let proto = new Proxy({}, {
                  get(target, propertyKey, receiver) {
                    console.log('GET ' + propertyKey);
                    return target[propertyKey];
                  }
                });
                
                let obj = Object.create(proto);
                obj.foo // "GET foo"

                在上面的代码中,变量obj继承了proto这个Proxy实例,所以只要是与obj有关的读取操作,get方法都会被执行

          • set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = vproxy['foo'] = v,返回一个布尔值
              • 参数:
                • target:目标对象
                • propKey:属性名
                • value:属性值
                • receiver:Proxy 实例
              • let validator = {
                  set: function(obj, prop, value) {
                    if (prop === 'age') {
                      if (!Number.isInteger(value)) {
                        throw new TypeError('The age is not an integer');
                      }
                      if (value > 200) {
                        throw new RangeError('The age seems invalid');
                      }
                    }
                
                    // 对于满足条件的 age 属性以及其他属性,直接保存
                    obj[prop] = value;
                    return true;
                  }
                };
                
                let person = new Proxy({}, validator);
                
                person.age = 100;
                
                person.age // 100
                person.age = 'young' // 报错
                person.age = 300 // 报错

                set方法用来拦截赋值操作,可以使数据格式统一,避免格式不同所引发的报错,强制统一格式,提升逻辑的准确性

                             在 CodeSendBox 上尝试

        • has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值

        • deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值

        • ownKeys(target):拦截Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy)for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性

        • getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象

        • defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)Object.defineProperties(proxy, propDescs),返回一个布尔值

        • preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值

        • getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象

        • isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值

        • setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截

      • apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)proxy.call(object, ...args)proxy.apply(...)
          • 参数
            • target:目标对象
            • object:目标对象的上下文对象(this
            • args:目标对象的参数数组
          • var target = function () { return 'I am the target'; };
            var handler = {
              apply: function (target, ctx, args) {
                return 'I am the proxy';
              }
            };
            
            var p = new Proxy(target, handler);
            
            p()
            // "I am the proxy"

            apply方法用来拦截函数调用、call和apply操作,apply的目标对象是一个函数,当函数被调用时会进入apply函数中

            在 CodeSendBox 上尝试
          • 当执行call和apply操作时
          • var twice = {
              apply (target, ctx, args) {
                return Reflect.apply(...arguments) * 2;
              }
            };
            function sum (left, right) {
              return left + right;
            };
            var proxy = new Proxy(sum, twice);
            proxy(1, 2) // 6
            proxy.call(null, 5, 6) // 22
            proxy.apply(null, [7, 8]) // 30

            在 CodeSendBox 中尝试


      • construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)

 

  1. Proxy.revocable()
    • Proxy.revocable()方法返回一个可取消的 Proxy 实例,就像生命周期中的销毁期,对一个js元素的销毁
      • let {proxy, revoke} = Proxy.revocable(target, handler);
      • 参数:
        • target:表示所要拦截的目标对象(必输)
        • handler:handler参数也是一个对象,用来定制拦截行为(必输)
      • 返回参数:
        • proxy:Proxy实例
        • revoke:revoke属性是一个函数,用于取消Proxy实例
      • Proxy.revocable()的一个使用场景是,目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就收回代理权,不允许再次访问

  2. this问题(作用域)
    • 虽然 Proxy 可以代理针对目标对象的访问,但它不是目标对象的透明代理,即不做任何拦截的情况下,也无法保证与目标对象的行为一致。主要原因就是在 Proxy 代理的情况下,目标对象内部的this关键字会指向 Proxy 代理
    • const target = {
        m: function () {
          console.log(this === proxy);
        }
      };
      const handler = {};
      
      const proxy = new Proxy(target, handler);
      
      target.m() // false
      proxy.m()  // true

      在 CodeSendBox 上尝试

 

标签:ES6,target,对象,propKey,Proxy,拦截,proxy
来源: https://www.cnblogs.com/Function-cnblogs/p/15000307.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有