JavaScript代理Proxy的驚人力量
點(diǎn)擊上方 前端Q,關(guān)注公眾號(hào)
回復(fù)加群,加入前端Q技術(shù)交流群
今天我們要學(xué)習(xí)的是ECMAScript 6 Proxy。我們將在本文中涉及以下主題。
什么是代理
代理人在行動(dòng)
誰(shuí)使用代理
使用案例和實(shí)例
資源簡(jiǎn)介
開(kāi)始吧:)
什么是代理
正如MDN網(wǎng)站上所說(shuō)。
Proxy對(duì)象可以讓你為另一個(gè)對(duì)象創(chuàng)建一個(gè)代理,它可以攔截和重新定義該對(duì)象的基本操作。
在解釋什么是Proxy的時(shí)候,說(shuō)它可以創(chuàng)建一個(gè)Proxy,這有點(diǎn)搞笑。當(dāng)然,他們并沒(méi)有說(shuō)錯(cuò),但是我們可以簡(jiǎn)化這個(gè)說(shuō)法,讓它更加友好。
Proxy對(duì)象使你能夠包裝目標(biāo)對(duì)象 通過(guò)這樣可以攔截和重新定義該對(duì)象的基本操作。
基本上,它的意思是說(shuō),我們要把一個(gè)對(duì)象,用Proxy包裹起來(lái),這將允許我們創(chuàng)建一個(gè) "隱藏 "的門(mén),并控制所有對(duì)所需對(duì)象的訪問(wèn)。
一個(gè)小插曲,Proxy也是一種軟件設(shè)計(jì)模式,你一定要讀一讀(維基百科鏈接)。
一個(gè)Proxy的創(chuàng)建有兩個(gè)參數(shù)。
target: 你想包裹的原始對(duì)象(proxy)
handler:定義哪些操作將被攔截,以及如何重新定義被攔截的操作的對(duì)象,也可以調(diào)用 "陷阱"。
代碼
const target = {
message1: "hello",
message2: "everyone"
};
const handler = {};
const proxy = new Proxy(target, handler);
大多數(shù)瀏覽器都支持代理功能,但也有一些老的瀏覽器不支持(當(dāng)然是IE),你可以在這里查看完整的列表。google有一個(gè)代理的polyfill,但它不支持所有的代理功能。
現(xiàn)在知道了什么是Proxies,想看看能用它做什么。
代理人在行動(dòng)
讓我們想象一下,我們是一家銀行或一個(gè)憂心忡忡的女朋友。我們想知道每次銀行賬戶(hù)余額被訪問(wèn)和被通知的時(shí)間。我們將使用最簡(jiǎn)單的處理程序操作/trap: get
const bankAccount = {
balance: 2020,
name: 'Georgy Glezer'
};
const handler = {
get: function(target, prop, receiver) {
if (prop === 'balance') {
console.log(`Current Balance Of: ${target.name} Is: ${target.balance} `);
}
return target[prop];
}
};
const wrappedBankAcount = new Proxy(bankAccount, handler);
wrappedBankAcount.balance; // access to the balance
// OUTPUT:
// Current Balance Of: Georgy Glezer Is: 2020
// 2020
在上面的例子中,我們有一個(gè)銀行賬戶(hù)對(duì)象,里面有我的名字和2020的余額。
這次的處理者對(duì)象是實(shí)現(xiàn)get操作/trap,它接收一個(gè)有3個(gè)參數(shù)的函數(shù)和get的返回值。
target: 被訪問(wèn)的對(duì)象(我們封裝的對(duì)象)。
prop:被訪問(wèn)的對(duì)象(我們封裝的對(duì)象)。在我們的例子中被訪問(wèn)的屬性,這里是 "balance"。
receiver:接收者。可以是代理,也可以是繼承自代理的對(duì)象。
我們定義了一個(gè)條件,如果被訪問(wèn)的屬性是 "余額",我們將通知(log)余額和當(dāng)前用戶(hù)名,并返回 "余額 "屬性。
從輸出中可以看到,一旦 "balance "屬性被訪問(wèn),我們就通過(guò)使用Proxy和設(shè)置get操作/陷阱,很容易地通知(log)了這次訪問(wèn)。
繼續(xù)我們銀行的想法,要求每次有人從銀行賬戶(hù)中取錢(qián),我們都要得到通知。而另一個(gè)約束條件是,銀行不允許出現(xiàn)負(fù)余額。為了達(dá)到這個(gè)目的,我們這次要使用設(shè)置處理程序/陷阱。
const bankAccount = {
balance: 2020,
name: 'Georgy Glezer'
};
const handler = {
set: function (obj, prop, value) {
console.log(`Current Balance: ${obj.balance}, New Balance: ${value}`);
if (value < 0) {
console.log(`We don't allow Negative Balance!`);
return false;
}
obj[prop] = value;
return true;
}
};
const wrappedBankAcount = new Proxy(bankAccount, handler);
wrappedBankAcount.balance -= 2000; // access to the balance
console.log(wrappedBankAcount.balance);
wrappedBankAcount.balance -= 50; // access to the balance
console.log(wrappedBankAcount.balance);
// OUTPUT:
// Current Balance: 2020, New Balance: 20
// 20
// Current Balance: 20, New Balance: -30
// We don't allow Negative Balance!
// 20
在上面的例子中,我們通知當(dāng)前的余額和取款后的新余額,如果新的余額是負(fù)數(shù),我們也會(huì)通知并中止取款操作。
我們使用的是set operator/trap,它是一個(gè)返回布爾值(true/false)的函數(shù),用來(lái)判斷更新操作是否成功。它接收以下參數(shù)。
target: 被訪問(wèn)的對(duì)象(我們封裝的對(duì)象)。
prop.prop:被訪問(wèn)的對(duì)象(我們封裝的對(duì)象)。在我們的例子中,被訪問(wèn)的屬性是 "balance"。
值。應(yīng)該更新的新值。
receiver:接收器。賦值最初指向的對(duì)象。這通常是代理本身。但是set()處理程序也可以間接調(diào)用,通過(guò)原型鏈或其他各種方式。
你可以看到,它和get真的很相似,但只是多接收了1個(gè)新值的參數(shù)。
這2個(gè)操作符/陷阱是最常見(jiàn)的,如果你有興趣找到所有現(xiàn)有的操作符/陷阱,你可以在這里查看。
誰(shuí)使用代理
許多流行的庫(kù)都使用了這種技術(shù),例如。
MobX
Vue
沉浸式
還有更多......他們中的大多數(shù)人都利用了Proxies給我們帶來(lái)的驚人力量,并為我們提供了很棒的庫(kù)。
使用案例和實(shí)例
我們已經(jīng)看到,我們可以使用代理服務(wù)器來(lái)進(jìn)行。
登錄(通知銀行)
驗(yàn)證(阻止負(fù)值更新)
緩存
我們將再次使用get operator/trap,并將 "dollars "屬性添加到我們的對(duì)象中。在每次訪問(wèn) "dollars "屬性時(shí),我們將計(jì)算我們的余額價(jià)值多少美元。因?yàn)橛?jì)算可能是一個(gè)沉重的操作,我們希望盡可能多的Cache它。
const bankAccount = {
balance: 10,
name: 'Georgy Glezer',
get dollars() {
console.log('Calculating Dollars');
return this.balance *3.43008459;
}
};
let cache = {
currentBalance: null,
currentValue: null
};
const handler = {
get: function (obj, prop) {
if (prop === 'dollars') {
let value = cache.currentBalance !== obj.balance ? obj[prop] : cache.currentValue;
cache.currentValue = value;
cache.currentBalance = obj.balance;
return value;
}
return obj[prop];
}
};
const wrappedBankAcount = new Proxy(bankAccount, handler);
console.log(wrappedBankAcount.dollars);
console.log(wrappedBankAcount.dollars);
console.log(wrappedBankAcount.dollars);
console.log(wrappedBankAcount.dollars);
// OUTPUT:
// Calculating Dollars
// 34.3008459
// 34.3008459
// 34.3008459
// 34.3008459
正如你在例子中所看到的,我們有一個(gè)緩存對(duì)象,它保存著當(dāng)前的銀行余額和以美元為單位的余額價(jià)值。每次有人訪問(wèn) "dollar "屬性時(shí),我們都會(huì)先進(jìn)行計(jì)算,然后將其緩存起來(lái)。
Dom操作
我們想在每次余額發(fā)生變化時(shí)更新屏幕上的文字。我們將使用一個(gè)set操作符/trap,每次改變數(shù)值時(shí),我們將更新屏幕上的DOM元素。
const bankAccount = {
balance: 2020,
name: "Georgy Glezer",
get text() {
return `${this.name} Balance Is: ${this.balance}`;
}
};
const objectWithDom = (object, domId) => {
const handler = {
set: function (obj, prop, value) {
obj[prop] = value;
document.getElementById(domId).innerHTML = obj.text;
return true;
}
};
return new Proxy(object, handler);
};
// create a dom element with id: bank-account
const wrappedBankAccount = objectWithDom(bankAccount, "bank-account");
wrappedBankAccount.balance = 26;
wrappedBankAccount.balance = 100000;
在這里,我們創(chuàng)建了一個(gè)輔助函數(shù),這樣我們就可以存儲(chǔ)DOM元素的ID,并在set operator/trap中添加了簡(jiǎn)單的行來(lái)更新DOM元素。很簡(jiǎn)單,對(duì)吧?讓我們看看結(jié)果:)

概要
綜上所述,我們了解了ECMAScript 6 Proxies,我們?nèi)绾问褂盟鼈儯约坝糜谑裁茨康摹T谖铱磥?lái),代理是一個(gè)神奇的工具,你可以用它來(lái)做各種各樣的選擇,你只需要考慮什么是最適合你的:) 。
資源
MDN Proxy
“Proxies are awesome” Brendan Eich presentation at JSConf
Proxy Design Pattern
關(guān)于本文 譯者:飄飄 作者:@Georgy Glezer 原文:https://levelup.gitconnected.com/the-amazing-power-of-javascript-proxies-aa27c6d06bcb
最后
歡迎加我微信,拉你進(jìn)技術(shù)群,長(zhǎng)期交流學(xué)習(xí)...
歡迎關(guān)注「前端Q」,認(rèn)真學(xué)前端,做個(gè)專(zhuān)業(yè)的技術(shù)人...
