理解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ì)象的底層操作,這些攔截行為通過(guò) trap 實(shí)現(xiàn)。每個(gè) trap 都可以覆蓋 JavaScript 對(duì)象的某些內(nèi)置行為,即 proxy 允許通過(guò) 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.
通過(guò) 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
性能測(cè)試
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)文章
自定義排序算法在JavaScript中的應(yīng)用小結(jié)
這篇文章主要介紹了自定義排序算法在JavaScript中的應(yīng)用,通過(guò)自定義排序函數(shù),我們能夠精確控制數(shù)組元素的排序邏輯,從而滿足各種復(fù)雜的應(yīng)用場(chǎng)景,需要的朋友可以參考下2024-12-12
用RadioButten或CheckBox實(shí)現(xiàn)div的顯示與隱藏
用RadioButten(或CheckBox)實(shí)現(xiàn)div的顯示與隱藏,當(dāng)選擇“女”時(shí),顯示“美女、才女”;當(dāng)選擇“男”時(shí)隱藏,具體實(shí)現(xiàn)如下,感興趣的朋友可以參考下2013-09-09
使用script的src實(shí)現(xiàn)跨域和類似ajax效果
在解決js的跨域問(wèn)題的時(shí)候, 有多種方式, 其中有一種是利用script標(biāo)簽的src屬性,因?yàn)檫@個(gè)屬性是不受域名限制的,我們可以直接讓src的這個(gè)鏈接指向跨域網(wǎng)站的一個(gè)接口, 這個(gè)接口返回的是js代碼或者json格式數(shù)據(jù), 從而實(shí)現(xiàn)跨域獲取數(shù)據(jù)。2014-11-11
基于Cesium實(shí)現(xiàn)衛(wèi)星在軌繞行動(dòng)畫
這篇文章主要為大家詳細(xì)介紹了如何利用Cesium實(shí)現(xiàn)衛(wèi)星在軌繞行動(dòng)畫,文中的示例代碼講解詳細(xì),對(duì)我們了解Cesium有一定的幫助,感興趣的可以嘗試一下2022-06-06
封裝一個(gè)公用Echarts圖表組件的3種模板代碼示例
這篇文章主要給大家介紹了關(guān)于封裝一個(gè)公用Echarts圖表組件的3種模板,定義圖表公共樣式是為了統(tǒng)一同一網(wǎng)站各頁(yè)面圖表的基礎(chǔ)樣式,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-02-02
通過(guò)JS判斷聯(lián)網(wǎng)類型和連接狀態(tài)的實(shí)現(xiàn)代碼
這篇文章主要介紹了通過(guò)JS判斷聯(lián)網(wǎng)類型和連接狀態(tài)的實(shí)現(xiàn)代碼,需要的朋友可以參考下2015-04-04
JavaScript Window瀏覽器對(duì)象模型原理解析
這篇文章主要介紹了JavaScript Window瀏覽器對(duì)象模型,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-05-05

