# [Javascript] 使用 Proxy 為 Object 代理進行前處理
# 藉由 Proxy, 我們可以為 Object 定義屬於自己的 Object 特性
最近工作上碰到一個很特別的需求。
由於公司的多國語言使用的是 JSON Object
來進行管理,上頭希望直接用 key 就可以當作是英文的多國語句,當有某個英文關鍵字沒有被設定時,希望能夠直接以 Object
的 key
當作內容輸出。
例如:
var obj = { | |
"a": "a", | |
"b": 1, | |
}; | |
console.log(obj["a"]); // "a" | |
console.log(obj["b"]); // 1 | |
console.log(obj["no setting key"]) // 正常應該要顯示 undefined,但是希望這邊顯示 "no setting key" |
這種違反行為的效果著實讓我傷透腦筋,直到後來發現了 ES6 的 Proxy
。
Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种 “元编程”(meta programming),即对编程语言进行编程。
Proxy 可以理解成,在目标对象之前架设一层 “拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来 “代理” 某些操作,可以译为 “代理器”。 — ECMAScript 6 入门
利用 Proxy
,我們可以擴增 Object 的功能或特性,做出屬於我們自己的物件特性。
Proxy 的使用格式為
obj = new Proxy(目標物件, handler物件);
所謂的 handler 物件
是用來做為 proxy
攔截 目標物件
而存在。因此我們必須在 handler object 中撰寫我們攔截 目標物件
後要進行的 getter
, setter
操作 function
。若 handler
沒有給予任何操作方法,則就跟一般物件無異。
以下給幾個範例:
賦值前的檢查
我們可以利用 proxy 來針對要賦值給予物件之前做檢查,以下面 code 為例:
setter 會接收三個參數 target Object
, property
, value
target object
: 要被賦值的目標物件。property
: 要被賦值的物件屬性 (也就是key
)value
: 要賦予的值
let person = new Proxy({}, { | |
set: (targetObj, prop, value) => { | |
if (prop === 'age') { | |
// 如果 要給值的 key 是 'age' 就事先檢查給予的值 | |
if (!Number.isInteger(value)) { | |
// 如果不是 integer 的話,就拋出 Error | |
throw new TypeError('The age is not an integer'); | |
} | |
if (value > 200) { | |
// 如果 年紀大於 200 的話,也不合理 | |
throw new RangeError('The age seems invalid'); | |
} | |
} | |
// 如果一切正常,則正常給值 | |
targetObj[prop] = value; | |
// 回傳 ok | |
return true; | |
} | |
}); |
上面的這段的結果,會讓 person
變為一個 Proxy
物件,並且在給值前進行檢查,如果給值的對象是 age , 則給的值 必須是不大於 200 的整數 ,否則會賦值失敗。
來看看結果
當我們輸入字串時,會拋出 The age is not an integer 的 error。
當我們輸入浮點數時一樣會拋出 The age is not an integer 的 error。
當我們給予超過 200 的整數時,也會被阻擋掉。
只有當我們給予正確範圍的整數時,才能成功賦值。
而當我們要對物件的 key
賦值時,並不會遭到阻擋。
我們將 person
印出來看看,發現其實是一個 Proxy
物件。
# 因此回到我們今天的主題
上頭希望直接用 key 就可以當作是英文的多國語句,當有某個英文關鍵字沒有被設定時,希望能夠直接以
Object
的key
當作內容輸出。
這該怎麼做呢?
依照 proxy 的思路,就很簡單了。
我們只要在取值時做檢查就可以了。
寫一個 getter function
var language = { | |
"I have a pen": "我有一支筆", | |
"I have an apple": "我有一顆蘋果", | |
} | |
language = new Proxy(language, { | |
get: (tar, prop, reveiver) => { | |
return ( !tar[prop] ) ? prop : tar[prop] | |
} | |
}) |
上面的 code 在做的事情就是,當 language
在取值前先進行檢查,若有值則直接回傳原值;否則回傳 key
值。
如此就達到了我們原本的目的了。
來看看結果