理解JavaScript中的Proxy 與 Reflection API
一、建立 Proxy
let target = {} let proxy = new Proxy(target,{}) proxy.name = "proxy" console.log(proxy.name) // proxy console.log(target.name) // proxy target.name = "target" console.log(proxy.name) // target console.log(target.name) // target
在上面的例子中,由 Proxy
構造器建立的 proxy
物件會將自身的所有操作直接轉發給 target
。
當 proxy.name
"proxy"
時,target
物件也會建立 name
屬性並獲得同樣的值。實際上 proxy
物件本身並不建立和儲存 name
屬性,它只是轉發對應的操作給 target
。
類似的,proxy.name
與 target.name
的值始終保持一致,因為它們實際上都指向了 target.name
。這也意味著給 target.name
賦予一個新的值時,該變化也會反映到 proxy.name
上。
使用 set Trap 驗證屬性
Proxy 允許開發者主動攔截本該轉發給 target 物件的底層操作,這些攔截行為通過 trap
實現。每個 trap
都可以覆蓋 JavaScript 物件的某些內建行為,即 proxy 允許通過 trap
假設需要建立一個新新增的屬性值只能是數字型別的物件,就可以藉助 set
trap 覆蓋預設的賦值行為。程式碼如下:
let target = { name: "target" } let proxy = new Proxy(target,{ set(trapTarget,key,value,receiver) { if (!trapTarget.hasOwnProperty(key)) { if (isNaN(value)) { throw new TypeError("New property must be a number.") } } return Reflect.set(trapTarget,receiver) } }) proxy.count = 1 console.log(proxy.count) // 1 console.log(target.count) // 1 proxy.name = "proxy" console.log(proxy.name) // proxy console.log(target.name) // proxy proxy.anotherName = "proxy" // TypeError: New property must be a number.
set trap 中的四個引數含義如下:
trapTarget
:接收新屬性的物件(即 proxy 指向的 target)key
:新屬性對應的 keyvalue
:新屬性對應的 valuereceiver
:通常為 proxy 自身
Reflect.set()
是與 set
trap 相對應的原始方法,表示被覆蓋前的預設的賦值行為。
使用 get Trap 令程式讀取不存在屬性時報錯
JavaScript 在讀取不存在的屬性時並不會報錯,而是返回 undefined
。
let target = {} console.log(target.name) // undefined
可以藉助 get
trap 修改讀取物件屬性時的預設行為:
let proxy = new Proxy({},{ get(trapTarget,receiver) { if (!(key in receiver)) { throw new TypeError("Property " + key + " doesn't exist.") } return Reflect.get(trapTarget,receiver) } }) proxy.name = "proxy" console.log(proxy.name) // proxy console.log(proxy.nme) // TypeError: Property nme doesn't exist.
通過 deleteProperty Trap 防止刪除屬性
JavaScript 中使用 delete
操作符刪除物件的屬性:
let target = { name: "target",value: 42 } Object.defineProperty(target,"name",{ configurable: false }) console.log("value" in target) // true let result1 = delete target.value console.log(result1) // true console.log("value" in target) // false let result2 = delete target.name console.log(result2) // false console.log("name" in target) // true
使用 deleteProxy Trap 防止屬性被意外刪除:
let target = { name: "target",value: 42 } let proxy = new Proxy(target,{ deleteProperty(trapTarget,key) { if (key === "value") { return false } else { return Reflect.deleteProperty(trapTarget,key) } } }) console.log("value" in proxy) // true let result1 = delete proxy.value console.log(result1) // false console.log("value" in proxy) // true let result2 = delete proxy.name console.log(result2) // true console.log("name" in proxy) // false
二、Proxy 的現實應用
logging
function makeLoggable(target) { return new Proxy(target,{ get: (target,property) => { console.log("Reading " + property) return target[property] },set: (target,property,value) => { console.log("Writing value " + value + " to " + property) target[property] = value } }) } let ninja = { name: "Yoshi" } ninja = makeLoggable(ninja) console.log(ninja.name) ninja.weapon = "sword" // Reading name // Yoshi // Writing value sword to weapon
效能測試
function isPrime(number) { if (number < 2) { return false } for (let i = 2; i < number; i++) { if (number % i === 0) { return false } } return true } isPrime = new Proxy(isPrime,{ apply: (target,thisArg,args) => { console.time("isPrime") const result = target.apply(thisArg,args) console.timeEnd("isPrime") return result } }) console.log(isPrime(1358765377)) // isPrime: 6815.107ms // true
自動新增屬性
function Folder() { return new Proxy({},property) => { console.log("Reading " + property) if(!(property in target)) { target[property] = new Folder() } return target[property] } }) } const rootFolder = new Folder() rootFolder.ninjasDir.firstNinjaDir.ninjaFile = "yoshi.txt" // Reading ninjasDir // Reading firstNinjaDir console.log(rootFolder.ninjasDir.firstNinjaDir.ninjaFile) // Reading ninjasDir // Reading firstNinjaDir // Reading ninjaFile // yoshi.txt
參考資料
https://leanpub.com/understandinges6
https://www.manning.com/books/secrets-of-the-javascript-ninja-second-edition
以上就是理解JavaScript中的Proxy 與 Reflection API的詳細內容,更多關於JavaScript中的Proxy 與 Reflection API的資料請關注我們其它相關文章!