理解JavaScript中的Proxy 與 Reflection API
一、創(chuàng)建 Proxy
let target = {}let proxy = new Proxy(target, {})proxy.name = 'proxy'console.log(proxy.name) // proxyconsole.log(target.name) // proxytarget.name = 'target'console.log(proxy.name) // targetconsole.log(target.name) // target
在上面的例子中,由 Proxy 構(gòu)造器創(chuàng)建的 proxy 對象會將自身的所有操作直接轉(zhuǎn)發(fā)給 target。當(dāng) proxy.name 被賦值為 'proxy' 時(shí),target 對象也會創(chuàng)建 name 屬性并獲得同樣的值。實(shí)際上 proxy 對象本身并不創(chuàng)建和存儲 name 屬性,它只是轉(zhuǎn)發(fā)對應(yīng)的操作給 target。
類似的,proxy.name 與 target.name 的值始終保持一致,因?yàn)樗鼈儗?shí)際上都指向了 target.name。這也意味著給 target.name 賦予一個(gè)新的值時(shí),該變化也會反映到 proxy.name 上。
使用 set Trap 驗(yàn)證屬性
Proxy 允許開發(fā)者主動攔截本該轉(zhuǎn)發(fā)給 target 對象的底層操作,這些攔截行為通過 trap 實(shí)現(xiàn)。每個(gè) trap 都可以覆蓋 JavaScript 對象的某些內(nèi)置行為,即 proxy 允許通過 trap 攔截并修改指向 target 對象的操作。
假設(shè)需要創(chuàng)建一個(gè)新添加的屬性值只能是數(shù)字類型的對象,就可以借助 set trap 覆蓋默認(rèn)的賦值行為。代碼如下:
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, key, value, receiver) }})proxy.count = 1console.log(proxy.count) // 1console.log(target.count) // 1proxy.name = 'proxy'console.log(proxy.name) // proxyconsole.log(target.name) // proxyproxy.anotherName = 'proxy'// TypeError: New property must be a number.
set trap 中的四個(gè)參數(shù)含義如下:
trapTarget:接收新屬性的對象(即 proxy 指向的 target) key:新屬性對應(yīng)的 key value:新屬性對應(yīng)的 value receiver:通常為 proxy 自身Reflect.set() 是與 set trap 相對應(yīng)的原始方法,表示被覆蓋前的默認(rèn)的賦值行為。
使用 get Trap 令程序讀取不存在屬性時(shí)報(bào)錯
JavaScript 在讀取不存在的屬性時(shí)并不會報(bào)錯,而是返回 undefined。
let target = {}console.log(target.name) // undefined
可以借助 get trap 修改讀取對象屬性時(shí)的默認(rèn)行為:
let proxy = new Proxy({}, { get(trapTarget, key, receiver) { if (!(key in receiver)) { throw new TypeError('Property ' + key + ' doesn’t exist.') } return Reflect.get(trapTarget, key, receiver) }})proxy.name = 'proxy'console.log(proxy.name) // proxyconsole.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) // truelet result1 = delete target.valueconsole.log(result1) // trueconsole.log('value' in target) // falselet result2 = delete target.nameconsole.log(result2) // falseconsole.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) // truelet result1 = delete proxy.valueconsole.log(result1) // falseconsole.log('value' in proxy) // truelet result2 = delete proxy.nameconsole.log(result2) // trueconsole.log('name' in proxy) // false
二、Proxy 的現(xiàn)實(shí)應(yīng)用
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({}, { get: (target, 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 firstNinjaDirconsole.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的詳細(xì)內(nèi)容,更多關(guān)于JavaScript中的Proxy 與 Reflection API的資料請關(guān)注好吧啦網(wǎng)其它相關(guān)文章!
相關(guān)文章:
1. php使用正則驗(yàn)證密碼字段的復(fù)雜強(qiáng)度原理詳細(xì)講解 原創(chuàng)2. Jsp+Servlet實(shí)現(xiàn)文件上傳下載 文件列表展示(二)3. 基于PHP做個(gè)圖片防盜鏈4. XML在語音合成中的應(yīng)用5. Jsp servlet驗(yàn)證碼工具類分享6. HTML5實(shí)戰(zhàn)與剖析之觸摸事件(touchstart、touchmove和touchend)7. 基于javaweb+jsp實(shí)現(xiàn)企業(yè)車輛管理系統(tǒng)8. ASP將數(shù)字轉(zhuǎn)中文數(shù)字(大寫金額)的函數(shù)9. asp.net core 認(rèn)證和授權(quán)實(shí)例詳解10. jscript與vbscript 操作XML元素屬性的代碼
