ES6 學(xué)習(xí)筆記(9) Proxy

1. 概述

  • Proxy 可以理解成,在目標(biāo)對象之前架設(shè)一層“攔截”,外界對該對象的訪問,都必須先通過這層攔截,因此提供了一種機(jī)制,可以對外界的訪問進(jìn)行過濾和改寫。Proxy 這個詞的原意是代理,用在這里表示由它來“代理”某些操作,可以譯為“代理器”。
  • Proxy 對象的所有用法,都是上面這種形式,不同的只是handler參數(shù)的寫法。其中,new Proxy()表示生成一個Proxy實(shí)例,target參數(shù)表示所要攔截的目標(biāo)對象,即如果沒有Proxy的介入,操作原來要訪問的就是這個對象;handler第二個參數(shù)是一個配置對象,對于每一個被代理的操作,需要提供一個對應(yīng)的處理函數(shù),該函數(shù)將攔截對應(yīng)的操作。
  var proxy = new Proxy({}, {
    get: function(target, property) {
      return 35;
    }
  });

  proxy.time // 35
  proxy.name // 35
  proxy.title // 35

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

  • get(target, propKey, receiver):攔截對象屬性的讀取,接收三個參數(shù),依次為目標(biāo)對象、屬性名和 proxy 實(shí)例本身(即this關(guān)鍵字指向的那個對象),其中最后一個參數(shù)可選

  • set(target, propKey, value, receiver):用來攔截某個屬性的賦值操作,可以接受四個參數(shù),依次為目標(biāo)對象、屬性名、屬性值和 Proxy 實(shí)例本身,其中最后一個參數(shù)可選。

  • has(target, propKey):has方法用來攔截HasProperty操作,即判斷對象是否具有某個屬性時(shí),這個方法會生效。典型的操作就是in運(yùn)算符。

  • deleteProperty(target, propKey):方法用于攔截delete操作,如果這個方法拋出錯誤或者返回false,當(dāng)前屬性就無法被delete命令刪除。

  • ownKeys(target):攔截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy),返回一個數(shù)組。該方法返回目標(biāo)對象所有自身的屬性的屬性名,而Object.keys()的返回結(jié)果僅包括目標(biāo)對象自身的可遍歷屬性。

  • getOwnPropertyDescriptor(target, propKey):攔截Object.getOwnPropertyDescriptor(),返回一個屬性描述對象或者undefined。

  • defineProperty(target, propKey, propDesc)defineProperty方法攔截了Object.defineProperty操作。返回false,導(dǎo)致添加新屬性會拋出錯誤

  • preventExtensions(target):攔截Object.preventExtensions()。該方法必須返回一個布爾值,否則會被自動轉(zhuǎn)為布爾值。

  • getPrototypeOf(target):主要用來攔截獲取對象原型。具體來說,攔截下面這些操作。
    Object.prototype.__proto__
    Object.prototype.isPrototypeOf()
    Object.getPrototypeOf()
    Reflect.getPrototypeOf()
    instanceof

  • isExtensible(target):攔截Object.isExtensible(proxy),返回一個布爾值。
    setPrototypeOf(target, proto):攔截Object.setPrototypeOf(proxy, proto),返回一個布爾值。如果目標(biāo)對象是函數(shù),那么還有兩種額外操作可以攔截。

  • apply(target, object, args):apply方法可以接受三個參數(shù),分別是目標(biāo)對象、目標(biāo)對象的上下文對象(this)和目標(biāo)對象的參數(shù)數(shù)組。

  • construct(target, args):方法用于攔截new命令,方法可以接受兩個參數(shù)。target: 目標(biāo)對象 ,args:構(gòu)建函數(shù)的參數(shù)對象

3. Proxy.revocable()

Proxy.revocable方法返回一個可取消的 Proxy 實(shí)例。

  let target = {};
  let handler = {};

  let {proxy, revoke} = Proxy.revocable(target, handler);

  proxy.foo = 123;
  proxy.foo // 123

  revoke();
  proxy.foo // TypeError: Revoked

Proxy.revocable的一個使用場景是,目標(biāo)對象不允許直接訪問,必須通過代理訪問,一旦訪問結(jié)束,就收回代理權(quán),不允許再次訪問。

4. this 問題

雖然 Proxy 可以代理針對目標(biāo)對象的訪問,但它不是目標(biāo)對象的透明代理,即不做任何攔截的情況下,也無法保證與目標(biāo)對象的行為一致。主要原因就是在 Proxy 代理的情況下,目標(biāo)對象內(nèi)部的this關(guān)鍵字會指向 Proxy 代理。

5. 實(shí)例:Web 服務(wù)的客戶端

Proxy 對象可以攔截目標(biāo)對象的任意屬性,這使得它很合適用來寫 Web 服務(wù)的客戶端。

  const service = createWebService('http://example.com/data');

  service.employees().then(json => {
    const employees = JSON.parse(json);
    // ···
  });

上面代碼新建了一個 Web 服務(wù)的接口,這個接口返回各種數(shù)據(jù)。Proxy 可以攔截這個對象的任意屬性,所以不用為每一種數(shù)據(jù)寫一個適配方法,只要寫一個 Proxy 攔截就可以了。

  function createWebService(baseUrl) {
    return new Proxy({}, {
      get(target, propKey, receiver) {
        return () => httpGet(baseUrl+'/' + propKey);
      }
    });
  }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容