zoukankan      html  css  js  c++  java
  • ES6之主要知识点(十)Proxy

    Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。

    Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,

    可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

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

    var proxy = new Proxy(target, handler);

    Proxy 对象的所有用法,都是上面这种形式,不同的只是handler参数的写法。

    其中,new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。

    var proxy = new Proxy({}, {
      get: function(target, property) {
        return 35;
      }
    });
    
    proxy.time // 35
    proxy.name // 35
    proxy.title // 35

    第一个参数是所要代理的目标对象(上例是一个空对象),即如果没有Proxy的介入,操作原来要访问的就是这个对象;

    第二个参数是一个配置对象,对于每一个被代理的操作,需要提供一个对应的处理函数,该函数将拦截对应的操作。

     一个技巧是将 Proxy 对象,设置到object.proxy属性,从而可以在object对象上调用。

    利用 Proxy,可以将读取属性的操作(get),转变为执行某个函数,从而实现属性的链式操作。

    var pipe = (function () {
      return function (value) {
        var funcStack = [];
        var oproxy = new Proxy({} , {
          get : function (pipeObject, fnName) {
            if (fnName === 'get') {
              return funcStack.reduce(function (val, fn) {
                return fn(val);
              },value);
            }
            funcStack.push(window[fnName]);
            return oproxy;
          }
        });
    
        return oproxy;
      }
    }());
    
    var double = n => n * 2;
    var pow    = n => n * n;
    var reverseInt = n => n.toString().split("").reverse().join("") | 0;
    
    pipe(3).double.pow.reverseInt.get; // 63
    var object = { proxy: new Proxy(target, handler) };

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

    var proxy = new Proxy({}, {
      get: function(target, property) {
        return 35;
      }
    });
    
    let obj = Object.create(proxy);
    obj.time // 35

    对于可以设置、但没有设置拦截的操作,则直接落在目标对象上,按照原先的方式产生结果。

    (1)get(target, propKey, receiver)

    拦截对象属性的读取,比如proxy.fooproxy['foo']

    最后一个参数receiver是一个对象,可选,参见下面Reflect.get的部分。

    (2)set(target, propKey, value, receiver)

    拦截对象属性的设置,比如proxy.foo = vproxy['foo'] = v,返回一个布尔值。

    (3)has(target, propKey)

    拦截propKey in proxy的操作,返回一个布尔值。

    (4)deleteProperty(target, propKey)

    拦截delete proxy[propKey]的操作,返回一个布尔值。

    (5)ownKeys(target)

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

    (6)getOwnPropertyDescriptor(target, propKey)

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

    (7)defineProperty(target, propKey, propDesc)

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

    (8)preventExtensions(target)

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

    (9)getPrototypeOf(target)

    拦截Object.getPrototypeOf(proxy),返回一个对象。

    (10)isExtensible(target)

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

    (11)setPrototypeOf(target, proto)

    拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。

    如果目标对象是函数,那么还有两种额外操作可以拦截。

    (12)apply(target, object, args)

    拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)proxy.call(object, ...args)proxy.apply(...)

    (13)construct(target, args)

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

    set()

    set方法用来拦截某个属性的赋值操作。

    假定Person对象有一个age属性,该属性应该是一个不大于200的整数,那么可以使用Proxy保证age的属性值符合要求。

    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;
      }
    };
    
    let person = new Proxy({}, validator);
    
    person.age = 100;
    
    person.age // 100
    person.age = 'young' // 报错
    person.age = 300 // 报错

    上面代码中,由于设置了存值函数set,任何不符合要求的age属性赋值,都会抛出一个错误,这是数据验证的一种实现方法。

    利用set方法,还可以数据绑定,即每当对象发生变化时,会自动更新 DOM。

    有时,我们会在对象上面设置内部属性,属性名的第一个字符使用下划线开头,表示这些属性不应该被外部使用。

    结合getset方法,就可以做到防止这些内部属性被外部读写。

    var handler = {
      get (target, key) {
        invariant(key, 'get');
        return target[key];
      },
      set (target, key, value) {
        invariant(key, 'set');
        target[key] = value;
        return true;
      }
    };
    function invariant (key, action) {
      if (key[0] === '_') {
        throw new Error(`Invalid attempt to ${action} private "${key}" property`);
      }
    }
    var target = {};
    var proxy = new Proxy(target, handler);
    proxy._prop
    // Error: Invalid attempt to get private "_prop" property
    proxy._prop = 'c'
    // Error: Invalid attempt to set private "_prop" property
  • 相关阅读:
    [PyTorch 学习笔记] 8.1 图像分类简述与 ResNet 源码分析
    [PyTorch 学习笔记] 7.3 使用 GPU 训练模型
    [PyTorch 学习笔记] 7.2 模型 Finetune
    [PyTorch 学习笔记] 7.1 模型保存与加载
    [PyTorch 学习笔记] 6.2 Normalization
    【成都GamEver游戏公司诚邀服务器伙伴】【7~15k一年4次项目奖金】
    如何彻底隐藏iOS7应用的status bar
    更新证书错误Code Sign error: Provisioning profile ‘XXXX'can't be found
    leetcode@ [273] Integer to English Words (String & Math)
    geeksforgeeks@ Largest Number formed from an Array
  • 原文地址:https://www.cnblogs.com/myzy/p/7575855.html
Copyright © 2011-2022 走看看