跳至主要内容

Proxy and Reflect

Proxy

什麼是 Proxy?

Proxy 是 ES6 (ECMAScript 2015) 引入的一個強大功能。它讓我們能夠創建一個「代理」物件,這個代理物件可以攔截並重新定義對另一個物件(目標物件)的各種操作。簡單來說,Proxy 讓我們可以更靈活地控制物件的行為,甚至在操作發生之前先進行處理。

和傳統 getter/setter 的比較

傳統的 gettersetter 只能在物件的特定屬性上使用,而 Proxy 則可以針對整個物件進行攔截,並能應對更廣泛的場景。以下是 Proxy 和傳統 getter/setter 的一些比較:

特性傳統 get/set 方法Proxy
應用範圍針對單一屬性,必須逐個定義可攔截整個物件的所有屬性
動態屬性處理主要適用於已知屬性,對新增屬性處理較麻煩可動態處理所有屬性的讀取和修改
全局攔截和控制只能處理屬性的讀取和賦值可攔截多種物件操作,如屬性刪除、檢查
原型污染可能會污染或覆蓋物件原型不會直接修改物件,避免影響原型鏈
邏輯封裝邏輯分散,維護較為困難可以集中管理,邏輯更清晰易懂

基本範例

const target = {
name: "Kevin",
age: 30,
};

const handler = {
get: function (target, prop, receiver) {
console.log(`Getting property ${prop}`);
return target[prop];
},
set: function (target, prop, value, receiver) {
console.log(`Setting property ${prop} to ${value}`);
target[prop] = value;
},
};

const proxy = new Proxy(target, handler);

console.log(proxy.name); // Kevin
proxy.age = 31; // Setting property age to 31
console.log(proxy.age); // 31

Reflect

什麼是 Reflect?

Reflect 是 ES6 引入的內建全局物件,它提供了一系列的靜態方法,專門用來操作 JavaScript 的物件。這些方法涵蓋了對物件的常見操作,例如屬性的讀取、設置、刪除等。與傳統的 Object 方法不同,Reflect 的方法具有更一致的行為,並且回傳的結果更具預測性。可以把 Reflect 看作是一個專門處理物件操作的工具箱。

為什麼需要 Reflect?

Reflect 出現之前,JavaScript 中操作物件的方法散落在各個內建物件中,如 ObjectFunction 等。有些方法的行為不夠一致,有時會拋出錯誤,有時回傳 undefined,這給開發者帶來了一定的困擾。隨著 Proxy 的引入,開發者需要一個統一的方式來處理代理物件的各種操作,這就是 Reflect 誕生的原因。

Reflect 的優勢

  • 統一的 APIReflect 提供了一組統一的方法,使得物件操作更加一致和可預測。
  • 更好的可讀性Reflect 的方法名很具描述性,讓程式更容易理解。
  • 更好的可擴展性Reflect 方法的回傳值通常是布爾值或具體結果,使得程式更易於處理錯誤和進一步擴展。

Reflect 的方法

Reflect 提供了 13 個靜態方法,以下是一些常用的方法:

  • Reflect.get(target, propertyKey, receiver):讀取目標物件的屬性值,類似於 target[propertyKey]
  • Reflect.set(target, propertyKey, value, receiver):設置目標物件的屬性值,類似於 target[propertyKey] = value
  • Reflect.has(target, propertyKey):檢查目標物件是否包含指定的屬性,類似於 propertyKey in target
  • Reflect.deleteProperty(target, propertyKey):刪除目標物件的指定屬性,類似於 delete target[propertyKey]
  • Reflect.ownKeys(target):回傳目標物件的所有自身屬性的鍵名,包括符號屬性和不可枚舉屬性。

Reflect 和 Proxy 的關係

ReflectProxy 是相輔相成的工具。Proxy 讓我們能夠攔截和自定義物件的行為,而 Reflect 則提供了處理這些行為的標準方法。在設置 Proxy 的 handler 時,我們可以使用 Reflect 的方法來實現預設的操作行為,這樣可以確保即使我們攔截了某些操作,物件的基礎功能依然能夠正常運作。

基本範例

const target = {
name: "Kevin",
age: 30,
};

const handler = {
get: function (target, prop, receiver) {
console.log(`Getting property ${prop}`);
return Reflect.get(target, prop, receiver);
},
set: function (target, prop, value, receiver) {
console.log(`Setting property ${prop} to ${value}`);
return Reflect.set(target, prop, value, receiver);
},
};

const proxy = new Proxy(target, handler);

console.log(proxy.name); // Getting property name, Kevin
proxy.age = 31; // Setting property age to 31
console.log(proxy.age); // Getting property age, 31

參考資料