ES6中的代理模式-----Proxy

玉鴦發表於2018-01-07

ES6中的代理模式-----Proxy

什麼是代理模式

代理模式(英語:Proxy Pattern)是程式設計中的一種設計模式。

所謂的代理者是指一個類別可以作為其它東西的介面。代理者可以作任何東西的介面:網路連線、記憶體中的大物件、檔案或其它昂貴或無法複製的資源。

著名的代理模式例子為引用計數(英語:reference counting)指標物件。

當一個複雜物件的多份副本須存在時,代理模式可以結合享元模式以減少記憶體用量。典型作法是建立一個複雜物件及多個代理者,每個代理者會引用到原本的複雜物件。而作用在代理者的運算會轉送到原本物件。一旦所有的代理者都不存在時,複雜物件會被移除。

上面是維基百科中對代理模式的一個整體的定義.而在JavaScript中代理模式的具體表現形式就是ES6中的新增物件---Proxy

什麼是Proxy物件

在MDN上對於Proxy的解釋是:

Proxy 物件用於定義基本操作的自定義行為(如屬性查詢,賦值,列舉,函式呼叫等)。

  簡單來說:Proxy物件就是可以讓你去對JavaScript中的一切合法物件的基本操作進行自定義.然後用你自定義的操作去覆蓋其物件的基本操作.也就是當一個物件去執行一個基本操作時,其執行的過程和結果是你自定義的,而不是物件的.

?好吧,用文字表達可能太複雜了.我們還是直接上程式碼吧.

首先Proxy的語法是:

let p = new Proxy(target, handler);
複製程式碼

其中:

  • target是你要代理的物件.它可以是JavaScript中的任何合法物件.如: (陣列, 物件, 函式等等)

  • handler是你要自定義操作方法的一個集合.

  • p是一個被代理後的新物件,它擁有target的一切屬性和方法.只不過其行為和結果是在handler中自定義的.

然後讓我們來看這段程式碼:

let obj = {
  a: 1,
  b: 2,
}

const p = new Proxy(obj, {
  get(target, key, value) {
    if (key === 'c') {
      return '我是自定義的一個結果';
    } else {
      return target[key];
    }
  },

  set(target, key, value) {
    if (value === 4) {
      target[key] = '我是自定義的一個結果';
    } else {
      target[key] = value;
    }
  }
})

console.log(obj.a) // 1
console.log(obj.c) // undefined
console.log(p.a) // 1
console.log(p.c) // 我是自定義的一個結果

obj.name = '李白';
console.log(obj.name); // 李白
obj.age = 4;
console.log(obj.age); // 4

p.name = '李白';
console.log(p.name); // 李白
p.age = 4;
console.log(p.age); // 我是自定義的一個結果
複製程式碼

從上面這段程式碼中,我可以很清楚的看到Proxy物件的作用.即是之前所受的用於定義基本操作的自定義行為.同樣的getset操作.沒有沒代理的物件所得的結果是其JavaScript本身的執行機制執行計算後所得到的.而被代理了的物件的結果則是我們自定義的.

Proxy所能代理的範圍--handler

在上面程式碼中,我們看到了構造一個代理物件時所傳的第二個引數handler,這個handler物件是由getset兩個函式方法組成的.這兩個方法會在一個物件被getset時被呼叫執行,以代替原生物件上的操作.那麼為什麼在handler,定義getset這兩個函式名之後就代理物件上的getset操作了呢?

實際上handler本身就是ES6所新設計的一個物件.它的作用就是用來自定義代理物件的各種可代理操作。它本身一共有13中方法,每種方法都可以代理一種操作.其13種方法如下:

handler.getPrototypeOf()

// 在讀取代理物件的原型時觸發該操作,比如在執行 Object.getPrototypeOf(proxy) 時。

handler.setPrototypeOf()

// 在設定代理物件的原型時觸發該操作,比如在執行 Object.setPrototypeOf(proxy, null) 時。

handler.isExtensible()

// 在判斷一個代理物件是否是可擴充套件時觸發該操作,比如在執行 Object.isExtensible(proxy) 時。

handler.preventExtensions()

// 在讓一個代理物件不可擴充套件時觸發該操作,比如在執行 Object.preventExtensions(proxy) 時。

handler.getOwnPropertyDescriptor()

// 在獲取代理物件某個屬性的屬性描述時觸發該操作,比如在執行 Object.getOwnPropertyDescriptor(proxy, "foo") 時。

handler.defineProperty()

// 在定義代理物件某個屬性時的屬性描述時觸發該操作,比如在執行 Object.defineProperty(proxy, "foo", {}) 時。

handler.has()

// 在判斷代理物件是否擁有某個屬性時觸發該操作,比如在執行 "foo" in proxy 時。

handler.get()

// 在讀取代理物件的某個屬性時觸發該操作,比如在執行 proxy.foo 時。

handler.set()

// 在給代理物件的某個屬性賦值時觸發該操作,比如在執行 proxy.foo = 1 時。

handler.deleteProperty()

// 在刪除代理物件的某個屬性時觸發該操作,比如在執行 delete proxy.foo 時。

handler.ownKeys()

// 在獲取代理物件的所有屬性鍵時觸發該操作,比如在執行 Object.getOwnPropertyNames(proxy) 時。

handler.apply()

// 在呼叫一個目標物件為函式的代理物件時觸發該操作,比如在執行 proxy() 時。

handler.construct()

// 在給一個目標物件為建構函式的代理物件構造例項時觸發該操作,比如在執行new proxy() 時。
複製程式碼

Proxy的作用

對於代理模式Proxy的作用主要體現在三個方面:

1、 攔截和監視外部對物件的訪問

2、 降低函式或類的複雜度

2、 在複雜操作前對操作進行校驗或對所需資源進行管理

而對於這三個使用方面的具體表現大家可以參考這篇文章--例項解析ES6 Proxy使用場景

Proxy的相容性

ES6中的代理模式-----Proxy

上面是我的一些粗淺的總結,希望對大家有所幫助.如果文中有何不當之處請予以斧正,謝謝.

參考資料:

我的個人網址: www.wangyiming19950222.com

相關文章