ES6新特性之Proxy

基本概念

字面意思的理解就是代理。編程

用於定義基本操做的自定義行爲,就是咱們能夠自定義某些行爲,好比屬性的查找,賦值,枚舉,函數調用等。
實際上咱們利用這個Proxy實現對編程語言進行編程,就是把一些內部的方式,內置的方法改變了,這種編程就叫作語言編程。屬性代理就作攔截。
關於Proxy須要注意的地方有:Proxy內部的this關鍵字的指向是Proxy代理自己;它的構建方式須要藉助一個Proxy的構造函數new Proxy(target,handler),其中target叫作目標對象,Proxy構造函數返回的是一個包裝事後的目標對象,handler是代理的行爲的函數。數組

let handler = {
        get: function(target, name) {
            return name in target ? target[name] : 'Eric';
        }
    };
    
    let p = new Proxy({}, handler);
    p.name;
    // Eric 
    
    
    let proxy = new Proxy({}, {
      get: function(target, property) {
        return 'Eric';
      }
    });
    
    let obj = Object.create(proxy);
    obj.name;
    // Eric    (說明Proxy是能夠繼承的)

代理操做

  • get, 攔截某個屬性的讀取操做,接收三個參數:target(目標對象)、property(屬性名)、receiver(通常是Proxy對象自己,可選參數)app

    let teacher = {
       name: "Eric"
     };
    
     let proxy = new Proxy(teacher, {
       get: function(target, property) {
         if (property in target) {
           return target[property];
         } else {
           throw new ReferenceError("\"" + property + "\" does not exist.");
         }
       }
     });
    
     proxy.name;
     // Eric 
     proxy.age;
     // ReferenceError: "age" does not exist.

get屬性攔截能夠繼承,須要注意的一點是,當某個對象不可配置(configurable)或者不可寫(writable),使用get會報錯。編程語言

  • set , 攔截某個屬性的賦值操做
    set屬性有四個參數:target(目標對象)、property(屬性名)、value(屬性值)、receiver(Proxy實例自己,可選參數)函數

    let handler = {
       set: function(obj, prop, value) {
         //若是不是一個整數就拋出一個錯誤‘The value must an integer’
         if (!Number.isInteger(value)) {
           throw new TypeError('The value must an integer');
         }
         /* document.getElementById(prop).innerHTML = value */
         obj[prop] = value;
       }
     };
    
     let teacher = new Proxy({}, handler);
     teacher.name = 'Eric';
     // TypeError: The value must an integer

    一樣的,在不可配置(configurable),不可寫(writable)的裏面,使用set不生效。this

  • apply , 函數調用、call和apply攔截
    apply接收三個參數:target(目標對象)、thisArg(目標對象this)、argumentsList(目標對象參數數組)代理

    var handler = {
        apply (target, ctx, args) {
          return Reflect.apply(...arguments);
        }
      };
    
    
      let target = function () {
        return 'I am Eric';
      };
    
      let handler = {
        apply: function () {
          return 'I am Iven';
        }
      };
    
      let teacher = new Proxy(target, handler);
      teacher();
      // I am Iven
  • has, 攔截對象是否具備某個屬性 - hasProperty
    has有兩個參數:target(目標對象)、prop(查詢的屬性名)。返回一個布爾值,true or false。code

    let handler = {
        has (target, key) {
          if (key.startsWith('_')) {
            return false;
          }
          return key in target;
        }
      };
      let target = {
        _property: 'private',
        property: 'public'
      };
    
      let proxy = new Proxy(target, handler);
      '_property' in proxy;
      // false

    注意的點:has不攔截for in循環;對象不可配置(configurable)時,使用has會報錯。orm

  • construct , 攔截new操做符
    construct接收三個參數:target(目標對象)、argumentsList(構造函數參數)、newTarget(最初被調用的構造函數,爲了方便可以獲得原來構造函數想要獲得的結構)。對象

    var handler = {
        construct (target, args, newTarget) {
          return new target(...args);
        }
      };
    
    
      var teacher = new Proxy(
        function () {},
        {
          construct: function(target, args) {
            return { name: 'Eric' };
          }
        }
      );
      new teacher();
      // { name: 'Eric' }

    注意:返回值必須是對象。

  • ownKeys , 攔截屬性遍歷
    own只有一個參數:target(目標對象)。

    let target = {
        Eric: 100,
        Iven: 200,
      };
    
      let handler = {
        ownKeys(target) {
          return Reflect.ownKeys(target).filter(key => target[key] < 150);
        }
      };
    
      let proxy = new Proxy(target, handler);
      Object.keys(proxy);
      // Eric
  • deleteProperty , 攔截刪除操做
    deleteProperty接收兩個參數:target(目標對象)、property(刪除的屬性名)。對象不可配置(configurable),使用deleteProperty會報錯

  • defineProperty , 攔截Object.defineProperty
    defineProperty接收三個參數:target(目標對象)、property(屬性名)、descriptor(描述樹)。屬性不可擴展(non-extensible)會報錯,不可配置(configurable)或者不可寫(writable)使用defineProperty不生效。

  • getOwnPropertyDescriptor , 攔截Object.getOwnPropertyDescriptor
    getOwnPropertyDescriptor接收兩個參數:target(目標對象)、prop(屬性名稱)。

  • getPrototypeOf , 攔截獲取對象原型
    getPrototypeOf 接收一個參數:target (目標對象)。必須返回對象或null。

  • isExtensible , 攔截Object.isExtensible
    getPrototypeOf 接收一個參數:target (目標對象)。必須返回布爾值。

  • preventExtensions , 攔截Object.preventExtensions
    preventExtensions 接收一個參數:target (目標對象)。必須返回一個布爾值。

  • setPrototypeOf , 攔截Object.setPrototypeOf
    setPrototypeOf 接收兩個參數:target (目標對象)、proto (原型對象)。

設置代理以後如何獲得默認的行爲?有兩種方式,一種是Proxy代理取消,另外一種是使用reflect。

Proxy代理取消

內部提供的方法:revocable

let target = {};
    let handler = {};
    let { proxy, revoke } = Proxy.revocable(target, handler);
    revoke();
    proxy.name;
    // ypeError: Cannot perform 'get' on a proxy that has been revoked
相關文章
相關標籤/搜索