理解JavaScript中的Proxy 與 Reflection API
一、創(chuàng)建 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
構(gòu)造器創(chuàng)建的 proxy
對(duì)象會(huì)將自身的所有操作直接轉(zhuǎn)發(fā)給 target
。
當(dāng) proxy.name
被賦值為 "proxy"
時(shí),target
對(duì)象也會(huì)創(chuàng)建 name
屬性并獲得同樣的值。實(shí)際上 proxy
對(duì)象本身并不創(chuàng)建和存儲(chǔ) name
屬性,它只是轉(zhuǎn)發(fā)對(duì)應(yīng)的操作給 target
。
類似的,proxy.name
與 target.name
的值始終保持一致,因?yàn)樗鼈儗?shí)際上都指向了 target.name
。這也意味著給 target.name
賦予一個(gè)新的值時(shí),該變化也會(huì)反映到 proxy.name
上。
使用 set Trap 驗(yàn)證屬性
Proxy 允許開發(fā)者主動(dòng)攔截本該轉(zhuǎn)發(fā)給 target 對(duì)象的底層操作,這些攔截行為通過 trap
實(shí)現(xiàn)。每個(gè) trap
都可以覆蓋 JavaScript 對(duì)象的某些內(nèi)置行為,即 proxy 允許通過 trap
攔截并修改指向 target 對(duì)象的操作。
假設(shè)需要?jiǎng)?chuàng)建一個(gè)新添加的屬性值只能是數(shù)字類型的對(duì)象,就可以借助 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 = 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 中的四個(gè)參數(shù)含義如下:
trapTarget
:接收新屬性的對(duì)象(即 proxy 指向的 target)key
:新屬性對(duì)應(yīng)的 keyvalue
:新屬性對(duì)應(yīng)的 valuereceiver
:通常為 proxy 自身
Reflect.set()
是與 set
trap 相對(duì)應(yīng)的原始方法,表示被覆蓋前的默認(rèn)的賦值行為。
使用 get Trap 令程序讀取不存在屬性時(shí)報(bào)錯(cuò)
JavaScript 在讀取不存在的屬性時(shí)并不會(huì)報(bào)錯(cuò),而是返回 undefined
。
let target = {} console.log(target.name) // undefined
可以借助 get
trap 修改讀取對(duì)象屬性時(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) // proxy console.log(proxy.nme) // TypeError: Property nme doesn't exist.
通過 deleteProperty Trap 防止刪除屬性
JavaScript 中使用 delete
操作符刪除對(duì)象的屬性:
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 的現(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
自動(dòng)添加屬性
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 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的詳細(xì)內(nèi)容,更多關(guān)于JavaScript中的Proxy 與 Reflection API的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
gridview生成時(shí)如何去掉style屬性中的border-collapse
默認(rèn)gridview控件會(huì)在生成的html代碼中的style屬性中加入border-collapse:collapse,如果想把它去掉的話2014-09-09基于JS實(shí)現(xiàn)簡單的隨機(jī)抽取幸運(yùn)員工系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了基于HTML+JavaScript實(shí)現(xiàn)簡單的隨機(jī)抽取幸運(yùn)員工系統(tǒng),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下2023-11-11(推薦一個(gè)超好的JS函數(shù)庫)S.Sams Lifexperience ScriptClassLib
(推薦一個(gè)超好的JS函數(shù)庫)S.Sams Lifexperience ScriptClassLib...2007-04-04理解Javascript的caller,callee,call,apply區(qū)別
理解Javascript的caller,callee,call,apply區(qū)別...2007-03-03javascript數(shù)組遍歷的方法實(shí)例分析
這篇文章主要介紹了javascript數(shù)組遍歷的方法,結(jié)合實(shí)例形式分析了javascript數(shù)組遍歷及相關(guān)的some、every、filter、map等方法的使用技巧,需要的朋友可以參考下2016-09-09