1. 程式人生 > >ES6之主要知識點(十)Proxy

ES6之主要知識點(十)Proxy

語言 () set receiver delet 賦值 target getproto apply

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

ES6之主要知識點(十)Proxy