JavaScript的Proxy代理對(duì)象,看完你就會(huì)了

什么是Proxy代理?
// pOjb就是通過(guò)new Proxy創(chuàng)建的代理對(duì)象var pObj = new Proxy(obj, handlers)
為什么需要代理對(duì)象
舉個(gè)記賬的例子:
// obj代表我們,wallet屬性指我們錢包,現(xiàn)在我們錢包里有100元// consume指消費(fèi)次數(shù),每次消費(fèi)加1, 記一筆賬var obj = {wallet: 100}var consume = 0// 這個(gè)月,我們喝了五次肥宅快樂水,每次消費(fèi)我們都記一筆// 今天消費(fèi)3元consume++obj.wallet = 97// 今天消費(fèi)3元consume++obj.wallet = 94// 今天消費(fèi)3元consume++obj.wallet = 91// 今天消費(fèi)3元consume++obj.wallet = 88// 今天消費(fèi)3元consume++obj.wallet = 85
每次我們修改錢包剩余金額時(shí),都要執(zhí)行一次consume++去執(zhí)行一次記賬的操作。有沒有更簡(jiǎn)單的方式,不需要每次都寫上一行代碼去增加消費(fèi)次數(shù)呢?
答案當(dāng)然有,它就是Proxy代理對(duì)象!使用代理對(duì)象,你想對(duì)目標(biāo)對(duì)象的屬性操作全部改為對(duì)代理對(duì)象相同屬性的操作,代理對(duì)象提供了對(duì)屬性獲取 [[get]] 修改 [[set]] 等操作的攔截,js將這種攔截稱為trap(捕捉器)。
通過(guò)捕捉器,我們就可以捕獲到 代碼中對(duì)屬性的操作時(shí)機(jī),讓我們能夠先執(zhí)行我們自定義的業(yè)務(wù)邏輯代碼。
因?yàn)槲覀儗?duì)目標(biāo)對(duì)象的屬性操作改為了對(duì)代理對(duì)象相同的屬性操作,所以我們?cè)谧詈笮枰ㄟ^(guò)Reflact執(zhí)行目標(biāo)對(duì)象的原始操作。
var consume = 0// 目標(biāo)對(duì)象var obj = {wallet: 100}// 捕獲器trapvar handlers = {set(target, key, val) {// target 目標(biāo)對(duì)象// key 代理對(duì)象要修改的屬性// 記錄一筆消費(fèi)consume++// 通過(guò)Reflact對(duì)象觸發(fā)原始目標(biāo)對(duì)象的屬性操作// 相當(dāng)于執(zhí)行 target[key] = valReflect.set(target, key, val)}}// 代理對(duì)象var pObj = new Proxy(obj, handlers)// 將對(duì)目標(biāo)對(duì)象obj的屬性wallet操作改為代理對(duì)象相同屬性wallet的操作pObj.wallet = 97pObj.wallet = 94pObj.wallet = 91pObj.wallet = 88pObj.wallet = 85console.log(obj.wallet) // 85console.log(consume) // 5
如何取消代理
假如某一天,你實(shí)現(xiàn)了財(cái)務(wù)自由,不需要再精打細(xì)算記錄每一筆消費(fèi)了,你可能就需要取消此前的代理,代碼很簡(jiǎn)單,往下看:
var consume = 0var obj = {wallet: 100}var handlers = {set(target, key, val) {consume++Reflect.set(target, key, val)}}// 使用Proxy.revocable創(chuàng)建代理var tmpObj = Proxy.revocable(obj, handlers)var pObj = tmpObj.proxyvar prevoke = tmpObj.revoke// 使用代理對(duì)象進(jìn)行消費(fèi)記賬pObj.wallet = 97pObj.wallet = 94pObj.wallet = 91// 某一天,我們實(shí)現(xiàn)了一個(gè)小目標(biāo)pObj.wallet = 100000000// 我們不需要記賬了,我們需要取消創(chuàng)建的代理prevoke() // 執(zhí)行prevoke即可,就是這么簡(jiǎn)單 哦耶~pObj.wallet = 99999997 // TypeError 報(bào)錯(cuò)啦 (代理取消之后就不能使用了喲!)
代理在后模式
前面的示例都是先執(zhí)行代理捕獲器中的業(yè)務(wù)邏輯,最后再通過(guò)Reflect執(zhí)行目標(biāo)對(duì)象的屬性操作,這種捕獲代碼操作在前,目標(biāo)對(duì)象操作在后的模式稱為“代理在先”模式,有在先,當(dāng)然就有在后模式。
當(dāng)然這里的“代理在后”模式并不是先使用Reflect對(duì)象觸發(fā)目標(biāo)對(duì)象屬性操作,在執(zhí)行捕獲器中的其他操作代碼。
而是指代理作為目標(biāo)對(duì)象的一種補(bǔ)充,我們?nèi)匀徊僮鞯氖悄繕?biāo)對(duì)象,只是當(dāng)某些操作在目標(biāo)對(duì)象上無(wú)法實(shí)現(xiàn)時(shí),才使用代理對(duì)象。
等會(huì),當(dāng)某些操作目標(biāo)對(duì)象無(wú)法提供時(shí),js會(huì)向目標(biāo)對(duì)象的原型prototype上進(jìn)行查找,所以“代理在后”模式是對(duì)目標(biāo)對(duì)象的原型進(jìn)行代理!
var handlers = {get(target, key, context) {return function () {context.speak(key + '!')}}}var catchall = new Proxy({}, handlers)var greeter = {speak(who = 'someone') {console.log('hello ', who)}}// 將catchall設(shè)置為greeter的原型Object.setPrototypeOf(greeter, catchall)greeter.speak() // hello someonegreeter.speak('world') // hello world// 執(zhí)行g(shù)reater上不存在的方法greeter.everyone() // hello everyone!
Reflect
Reflect對(duì)象用來(lái)觸發(fā)目標(biāo)對(duì)象執(zhí)行相應(yīng)的操作,就是這么簡(jiǎn)單!
Reflect.get(target, key, context) // 等價(jià)于 target[key]Reflect.set(target, key, val) // 等價(jià)于 target[key] = val
本文完~
學(xué)習(xí)更多技能
請(qǐng)點(diǎn)擊下方公眾號(hào)
![]()

