Proxy and Reflect
Proxy
什麼是 Proxy?
Proxy
是 ES6 (ECMAScript 2015) 引入的一個強大功能。它讓我們能夠創建一個「代理」物件,這個代理物件可以攔截並重新定義對另一個物件(目標物件)的各種操作。簡單來說,Proxy
讓我們可以更靈活地控制物件的行為,甚至在操作發生之前先進行處理。
和傳統 getter/setter 的比較
傳統的 getter
和 setter
只能在物件的特定屬性上使用,而 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 中操作物件的方法散落在各個內建物件中,如 Object
、Function
等。有些方法的行為不夠一致,有時會拋出錯誤,有時回傳 undefined
,這給開發者帶來了一定的困擾。隨著 Proxy
的引入,開發者需要一個統一的方式來處理代理物件的各種操作,這就是 Reflect
誕生的原因。
Reflect 的優勢
- 統一的 API:
Reflect
提供了一組統一的方法,使得物件操作更加一致和可預測。 - 更好的可讀性:
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 的關係
Reflect
和 Proxy
是相輔相成的工具。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