一、簡介
代理是一種基本設計模式,一種對象結構模式。被代理的對象可以是遠程對象、創建開銷大的對象或需要安全控制的對象等,代理對象在客戶端和目標對象之間起到中介作用,可以在目標對象被訪問前後進行一些操作,使得使用者無須關心目標對象的具體實現。
在 JavaScript 中,我們可以使用一個對象來代理另一個對象。而 ProxyJS 就是一個封裝了代理模式的 JavaScript 庫,旨在為 JavaScript 程序員提供一個擴展語言核心的機制,這個機制允許你定義一個對象,在其上定義一組操作,這些操作對這個對象執行的所有操作都會被攔截器處理。
二、基本用法
在使用 ProxyJS 時,我們需要創建一個代理實例,並在其上添加一些攔截器(Handlers)。比如下面的例子,我們使用 Proxy 代理了一個對象,然後在代理時添加了一個 `get` 攔截器,當試圖訪問代理實例的 `name` 屬性時,這個攔截器會被調用:
const obj = { name: "Alice" }; const p = new Proxy(obj, { get(target, property, receiver) { console.log("Someone is trying to access the property!"); return Reflect.get(target, property, receiver); } }); console.log(p.name); // 輸出 "Someone is trying to access the property!" 和 "Alice"
上面的代碼中,我們創建了一個普通對象 obj,並使用 Proxy 創建了一個代理實例 p。當我們嘗試訪問 p 的 `name` 屬性時,代理實例會觸發 `get` 攔截器,這個攔截器會在獲取 `name` 屬性值之前輸出一條日誌,然後再返回屬性值。
三、代理操作的攔截器
在上面的例子中我們已經使用了一個 `get` 攔截器,它會在代理對象上獲取屬性時被調用。ProxyJS 提供了一些預置的攔截器,它們會在執行一些操作時被調用,比如:
- get(target, property, receiver)
- set(target, property, value, receiver)
- has(target, property)
- deleteProperty(target, property)
- apply(target, thisArg, argumentsList)
- construct(target, argumentsList, newTarget)
- getPrototypeOf(target)
- setPrototypeOf(target, prototype)
- defineProperty(target, property, descriptor)
- getOwnPropertyDescriptor(target, property)
- … …
通過在代理對象上添加這些攔截器(Handlers),我們可以修改代理對象執行這些操作時的行為。比如下面的例子中,我們定義了一個代理對象 p,其中添加了一個 `set` 攔截器,在給代理對象設置屬性時,這個攔截器會將屬性的名稱轉換為小寫存儲,而不是原來的大寫:
const p = new Proxy({}, { set(target, property, value, receiver) { if (typeof property === 'string') { property = property.toLowerCase(); } return Reflect.set(target, property, value, receiver); } }); p.Foo = 'bar'; console.log(p.Foo); // 輸出 "bar" console.log(p.foo); // 輸出 "bar"
在上面的代碼中,當我們試圖在代理對象 p 上設置一個屬性時,`set` 攔截器會首先將屬性的名稱轉換為小寫,然後再將屬性保存到代理對象中。這樣一來,我們可以在屬性訪問時使用大小寫不敏感的方式,而且代理對象會始終使用小寫名稱存儲屬性。
四、反射器(Reflect API)
ProxyJS 還提供了一個反射器 API,可以讓我們在訪問代理對象時直接調用方法,而不需要使用攔截器。這個 API 的核心就是 Reflect 對象,它提供了一組操作代理對象的方法。
比如,我們可以使用 Reflect.get() 方法直接獲取代理對象的屬性值,而不需要定義一個 `get` 攔截器:
const obj = { name: "Alice" }; const p = new Proxy(obj, {}); console.log(Reflect.get(p, 'name')); // 輸出 "Alice"
在上面的代碼中,我們首先創建了一個普通對象 obj,然後將其傳遞給 Proxy 構造函數,創建了一個代理對象 p。最後,我們使用 Reflect.get() 方法獲取代理對象 p 的 `name` 屬性值(在這裡我們並沒有定義一個 `get` 攔截器),它將返回原始對象 obj 中的名稱屬性值。
五、應用場景
ProxyJS 可以在很多場景下使用。比如,我們可以將一個對象的訪問許可權限制為只讀,或者只允許訪問一部分屬性。
一個經典的案例就是攔截對象之間的依賴關係。通過一個對象重新包裝另一個對象,並在其屬性訪問時添加依賴關係,從而實現數據綁定等功能,可以較為輕鬆的實現數據層和視圖層的解耦。
let obj = {a:1, b:2}; let dependentObj = {x: 'Hello, '}; let mixinObj = Object.assign( {}, obj, { get a() { return obj.a; }, set a(value) { dependentObj.x += `a的值改變為:` + value; obj.a = value; } } ); mixinObj.a = 3; // 執行setter操作,觸發依賴關係 console.log(dependentObj.x); // 輸出 "Hello, a的值改變為:3"
在上面的代碼中,我們通過一個 mixinObj 對象重新包裝了 obj 對象,並添加了一個 get/set 攔截器,從而在 obj 對象的 `a` 屬性訪問時可以將這個訪問依賴添加到 dependentObj 對象的 `x` 屬性中。這種攔截器模式為數據層和視圖層的解耦提供了一種新的編程方式。
六、總結
ProxyJS 是一個很強大的庫,它允許 JavaScript 程序員重新定義 JavaScript 的對象操作。通過添加一些攔截器和反射器,我們可以在執行操作時做一些額外的工作,從而實現顛覆傳統對象訪問的編程方式。當然,ProxyJS 也有許多其他功能,比如可以用它來添加自定義的事件監聽器和事件分發器,還可以用它來實現像 Mixin 和 Traits 合成等高級編程技巧。
使用 ProxyJS 進行編程,你需要從面向對象的思維方式轉變為面向攔截器的方式,這可能會需要一些練習,但一旦掌握,它將可以為你提供很強的編程能力和靈活性。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/206108.html