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
相關文章
相關標籤/搜索