1. 程式人生 > >ES6語法和知識點:代理proxy

ES6語法和知識點:代理proxy

Proxy 可以理解成,在目標物件之前架設一層“攔截”,外界對該物件的訪問,都必須先通過這層攔截,因此提供了一種機制,可以對外界的訪問進行過濾和改寫。

var obj = new Proxy({}, {
  get: function (target, key, receiver) {
    console.log(`getting ${key}!`);
    return Reflect.get(target, key, receiver);
  },
  set: function (target, key, value, receiver) {
    console.log(`setting ${key}!`);
    return Reflect.set(target, key, value, receiver);
  }
});


//執行結果
obj.count = 1
//  setting count!
++obj.count
//  getting count!
//  setting count!
//  2

 S6 原生提供 Proxy 建構函式,用來生成 Proxy 例項。new Proxy()表示生成一個Proxy例項,target引數表示所要攔截的目標物件,handler引數也是一個物件,用來定製攔截行為。

created方法:

var object = { proxy: new Proxy(target, handler) };

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

let obj = Object.create(proxy);
obj.time // 35

下面是 Proxy 支援的攔截操作一覽,一共 13 種。

  • get(target, propKey, receiver):攔截物件屬性的讀取,比如proxy.fooproxy['foo']
  • set(target, propKey, value, receiver):攔截物件屬性的設定,比如proxy.foo = vproxy['foo'] = v,返回一個布林值。
  • 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(...)。 
  • construct(target, args):攔截 Proxy 例項作為建構函式呼叫的操作,比如new proxy(...args)

apply()

apply方法攔截函式的呼叫、callapply操作。

apply方法可以接受三個引數,分別是目標物件、目標物件的上下文物件(this)和目標物件的引數陣列。

 get方法用於攔截某個屬性的讀取操作,可以接受三個引數,依次為目標物件、屬性名和 proxy 例項本身(嚴格地說,是操作行為所針對的物件),其中最後一個引數可選。

set方法用來攔截某個屬性的賦值操作,可以接受四個引數,依次為目標物件、屬性名、屬性值和 Proxy 例項本身,其中最後一個引數可選。

set方法的第四個引數receiver,指的是原始的操作行為所在的那個物件,一般情況下是proxy例項本身,果目標物件自身的某個屬性,不可寫且不可配置,那麼set方法將不起作用。

has方法用來攔截HasProperty操作,即判斷物件是否具有某個屬性時,這個方construct方法用於攔截new命令,下面是攔截物件的寫法。法會生效。典型的操作就是in運算子。 

construct方法可以接受兩個引數。

  • target:目標物件
  • args:建構函式的引數物件
  • newTarget:創造例項物件時,new命令作用的建構函式
  • var handler = {
      construct (target, args, newTarget) {
        return new target(...args);
      }
    };

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

 有些原生物件的內部屬性,只有通過正確的this才能拿到,所以 Proxy 也無法代理這些原生物件的屬性。