理解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
對象會將自身的所有操作直接轉(zhuǎn)發(fā)給 target
。
當(dāng) proxy.name
被賦值為 "proxy"
時,target
對象也會創(chuàng)建 name
屬性并獲得同樣的值。實際上 proxy
對象本身并不創(chuàng)建和存儲 name
屬性,它只是轉(zhuǎn)發(fā)對應(yīng)的操作給 target
。
類似的,proxy.name
與 target.name
的值始終保持一致,因為它們實際上都指向了 target.name
。這也意味著給 target.name
賦予一個新的值時,該變化也會反映到 proxy.name
上。
使用 set Trap 驗證屬性
Proxy 允許開發(fā)者主動攔截本該轉(zhuǎn)發(fā)給 target 對象的底層操作,這些攔截行為通過 trap
實現(xiàn)。每個 trap
都可以覆蓋 JavaScript 對象的某些內(nèi)置行為,即 proxy 允許通過 trap
攔截并修改指向 target 對象的操作。
假設(shè)需要創(chuàng)建一個新添加的屬性值只能是數(shù)字類型的對象,就可以借助 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, 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 中的四個參數(shù)含義如下:
trapTarget
:接收新屬性的對象(即 proxy 指向的 target)key
:新屬性對應(yīng)的 keyvalue
:新屬性對應(yīng)的 valuereceiver
:通常為 proxy 自身
Reflect.set()
是與 set
trap 相對應(yīng)的原始方法,表示被覆蓋前的默認的賦值行為。
使用 get Trap 令程序讀取不存在屬性時報錯
JavaScript 在讀取不存在的屬性時并不會報錯,而是返回 undefined
。
let target = {} console.log(target.name) // undefined
可以借助 get
trap 修改讀取對象屬性時的默認行為:
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
操作符刪除對象的屬性:
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)實應(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 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的詳細內(nèi)容,更多關(guān)于JavaScript中的Proxy 與 Reflection API的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
自定義排序算法在JavaScript中的應(yīng)用小結(jié)
這篇文章主要介紹了自定義排序算法在JavaScript中的應(yīng)用,通過自定義排序函數(shù),我們能夠精確控制數(shù)組元素的排序邏輯,從而滿足各種復(fù)雜的應(yīng)用場景,需要的朋友可以參考下2024-12-12用RadioButten或CheckBox實現(xiàn)div的顯示與隱藏
用RadioButten(或CheckBox)實現(xiàn)div的顯示與隱藏,當(dāng)選擇“女”時,顯示“美女、才女”;當(dāng)選擇“男”時隱藏,具體實現(xiàn)如下,感興趣的朋友可以參考下2013-09-09使用script的src實現(xiàn)跨域和類似ajax效果
在解決js的跨域問題的時候, 有多種方式, 其中有一種是利用script標簽的src屬性,因為這個屬性是不受域名限制的,我們可以直接讓src的這個鏈接指向跨域網(wǎng)站的一個接口, 這個接口返回的是js代碼或者json格式數(shù)據(jù), 從而實現(xiàn)跨域獲取數(shù)據(jù)。2014-11-11通過JS判斷聯(lián)網(wǎng)類型和連接狀態(tài)的實現(xiàn)代碼
這篇文章主要介紹了通過JS判斷聯(lián)網(wǎng)類型和連接狀態(tài)的實現(xiàn)代碼,需要的朋友可以參考下2015-04-04