iT邦幫忙

2024 iThome 鐵人賽

DAY 22
0

Proxy 是一個 ES 6 引入的新物件,Proxy 一詞在中文中是「代理」的意思。

現實中的例子來舉例,比如老闆管理一家公司,日理萬機,可不是誰都能見到老闆的。
那由誰來決定哪些人可以見老闆?秘書這個職位就負責處理這些事情。秘書熟知老闆的行程,做事習慣,手上的工作等等,透過瞭解老闆的優先序,他能為老闆排出最適切的會面對象。

秘書就是 Proxy
秘書是一個中間人的角色,他並不能直接改變老闆,但可以對要到達老闆前的事情做一層攔截跟處理。

Proxy 在 JS 的宣告方式如下

const p = new Proxy(target, handler)

兩個參數分別是 targethandlertarget 用於傳入原始對象(要代理的物件,老闆),handler 用於定義代理的處理行為。

handler

JS 在對物件的操作上,多有定義內部方法(Internal Methods)來處理,這些方法我們沒辦法透過方法的名稱來使用,而是透過對應規範的語法來使用。
obj.name 實際上是對 obj 呼叫了內部方法 [[Get]],傳入屬性名稱並期望獲得物件上對應屬性的值。

一般來說內部方法會用 [[]] 來表示,由於不可呼叫性,如果對內部方法的實踐有興趣,就需要直接查閱引擎的原始碼(如 V8),才能了解如何實踐的,或是閱讀 ECMAScript,會有對應的規範描述。
我們通常會更熟知表面的行為如何反應而不去關注過多的內部實踐。

提到內部方法是因為 handler 主要的攔截對象就是對目標物件的操作。
handler 用於攔截的方法包括以下幾種,都對應到各種內部方法。

這些攔截方法被稱為攔截器(trap),當對目標物件用了對應的的方法,handler 就會被觸發且處理:

  1. apply:攔截 apply()
  2. construct:攔截 new
  3. defineProperty(), deleteProperty, getOwnPropertyDescriptor():攔截對應對屬性的操作
  4. has(), ownKeys(), :對應到攔截 inownKeys() 的操作
  5. get(), set():攔截對物件上屬性的讀取或寫入操作
  6. isExtensible(), preventExtensions():攔截對物件的原型鏈繼承的操作
  7. setPrototypeOf() :攔截對物件原型的操作

來看一個實際的例子:

//明星
const star = {
    name: 'Big Star',
    address: 'Moon Street',
    phone: '987-654-3210',
    nextConcert: '2024-10-06',
    getNextConcert() {
        return this.nextConcert;
    }
};

//經紀人
const agentHandler = {
//攔截對明星資訊的訪問
    get(target, property) {
        //只有當詢問演唱會時間才會回答
        if (property === 'nextConcert') {
            return target.getNextConcert();ㄥ
        } else {
            //其他一概不知道
            console.log(`Proxy get the request to : ${property} and block.`);
            return undefined;
        }
    }
};

//使用 Proxy 創建經紀人
const agent = new Proxy(star, agentHandler);

console.log(agent.nextConcert); // "2024-10-06"
console.log(agent.address); //"Proxy get the request to : address and block." undefined
console.log(agent.phone); // "Proxy get the request to : phone and block." undefined

代理使用例子

代理的攔截一般可以作用於幾種場景:

  1. 驗證傳入的資料或格式化:攔截 set,如果出現非預期的資料可以調整後再 set,或拋出錯誤。
  2. 設定預設值:攔截 construct,為建構參數塞入額外的東西,或是攔截 get 在值不存在的情況下返回特定值。

Proxy 其中一個有名的實踐就是 Vue.js(這裡指 Vue 3 以後的版本)。
有使用過 Vue.js 的人應該知道,Vue.js 是一套 JS 的框架,主要用於前端開發,MVVM 的架構讓開發者可以專注在操作後端的資料流,前端的元件就自動對應反應。

「改後端的資料,讓前端的元件自動對應反應」,聽起來是不是就像是代理的行為一樣?對,因為 Vue.js 就是為每個資料都建立對應的 Proxy 物件,攔截 getset 行為,這樣一旦資料更新,他便能對對應的 DOM元件JS物件,做定義的操作,從而達到響應式的畫面與程式。

Proxy 在對物件的監視、同步、攔截上都有很好效果,希望這篇能讓大家更熟悉這個 Proxy 的特性與使用範例。


上一篇
例外(Exception)、錯誤物件(Error)與攔截
下一篇
反射(Reflect)
系列文
Don't make JavaScript Just Surpise31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言