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