JavaScript 代理的驚人力量

今天我們要學習的是 ECMAScript 6 Proxies。我們將在本文中涉及以下主題。

開始吧:)

什麼是代理

正如 MDN 網站上所說。

Proxy 對象可以讓你爲另一個對象創建一個代理,它可以攔截和重新定義該對象的基本操作。

在解釋什麼是 Proxy 的時候,說它可以創建一個 Proxy,這有點搞笑。當然,他們並沒有說錯,但是我們可以簡化這個說法,讓它更加友好。

Proxy 對象使你能夠包裝目標對象 通過這樣可以攔截和重新定義該對象的基本操作。

基本上,它的意思是說,我們要把一個對象,用 Proxy 包裹起來,這將允許我們創建一個 "隱藏" 的門,並控制所有對所需對象的訪問。

一個小插曲,Proxy 也是一種軟件設計模式,你一定要讀一讀(維基百科鏈接)。

一個 Proxy 的創建有兩個參數。

代碼

大多數瀏覽器都支持代理功能,但也有一些老的瀏覽器不支持(當然是 IE),你可以在這裏查看完整的列表。google 有一個代理的 polyfill,但它不支持所有的代理功能。

現在知道了什麼是 Proxies,想看看能用它做什麼。

代理人在行動

讓我們想象一下,我們是一家銀行或一個憂心忡忡的女朋友。我們想知道每次銀行賬戶餘額被訪問和被通知的時間。我們將使用最簡單的處理程序操作 / trap: get

 1const bankAccount = {
 2  balance: 2020,
 3  name: 'Georgy Glezer'
 4};
 5const handler = {
 6   get: function(target, prop, receiver) {
 7    if (prop === 'balance') {
 8    console.log(`Current Balance Of: ${target.name} Is: ${target.balance} `);
 9    }
10    return target[prop];
11  }
12};
13const wrappedBankAcount = new Proxy(bankAccount, handler);
14wrappedBankAcount.balance; // access to the balance
15// OUTPUT:
16// Current Balance Of: Georgy Glezer Is: 2020
17// 2020
18

在上面的例子中,我們有一個銀行賬戶對象,裏面有我的名字和 2020 的餘額。

這次的處理者對象是實現 get 操作 / trap,它接收一個有 3 個參數的函數和 get 的返回值。

我們定義了一個條件,如果被訪問的屬性是 "餘額",我們將通知 (log) 餘額和當前用戶名,並返回 "餘額" 屬性。

從輸出中可以看到,一旦 "balance" 屬性被訪問,我們就通過使用 Proxy 和設置 get 操作 / 陷阱,很容易地通知 (log) 了這次訪問。

繼續我們銀行的想法,要求每次有人從銀行賬戶中取錢,我們都要得到通知。而另一個約束條件是,銀行不允許出現負餘額。爲了達到這個目的,我們這次要使用設置處理程序 / 陷阱。

 1const bankAccount = {
 2    balance: 2020,
 3    name: 'Georgy Glezer'
 4};
 5const handler = {
 6    set: function (obj, prop, value) {
 7        console.log(`Current Balance: ${obj.balance}, New Balance: ${value}`);
 8        if (value < 0) {
 9            console.log(`We don't allow Negative Balance!`);
10            return false;
11        }
12        obj[prop] = value;
13        return true;
14    }
15};
16const wrappedBankAcount = new Proxy(bankAccount, handler);
17wrappedBankAcount.balance -= 2000; // access to the balance
18console.log(wrappedBankAcount.balance);
19wrappedBankAcount.balance -= 50; // access to the balance
20console.log(wrappedBankAcount.balance);
21// OUTPUT:
22// Current Balance: 2020, New Balance: 20
23// 20
24// Current Balance: 20, New Balance: -30
25// We don't allow Negative Balance!
26// 20
27

在上面的例子中,我們通知當前的餘額和取款後的新餘額,如果新的餘額是負數,我們也會通知並中止取款操作。

我們使用的是 set operator/trap,它是一個返回布爾值 (true/false) 的函數,用來判斷更新操作是否成功。它接收以下參數。

你可以看到,它和 get 真的很相似,但只是多接收了 1 個新值的參數。

這 2 個操作符 / 陷阱是最常見的,如果你有興趣找到所有現有的操作符 / 陷阱,你可以在這裏查看。

誰使用代理

許多流行的庫都使用了這種技術,例如。

還有更多...... 他們中的大多數人都利用了 Proxies 給我們帶來的驚人力量,併爲我們提供了很棒的庫。

使用案例和實例

我們已經看到,我們可以使用代理服務器來進行。

緩存

我們將再次使用 get operator/trap,並將 "dollars" 屬性添加到我們的對象中。在每次訪問 "dollars" 屬性時,我們將計算我們的餘額價值多少美元。因爲計算可能是一個沉重的操作,我們希望儘可能多的 Cache 它。

 1const bankAccount = {
 2    balance: 10,
 3    name: 'Georgy Glezer',
 4    get dollars() {
 5        console.log('Calculating Dollars');
 6        return this.balance *3.43008459;
 7    }
 8};
 9let cache = {
10    currentBalance: null,
11    currentValue: null
12};
13const handler = {
14    get: function (obj, prop) {
15        if (prop === 'dollars') {
16            let value = cache.currentBalance !== obj.balance ? obj[prop] : cache.currentValue;
17            cache.currentValue = value;
18            cache.currentBalance = obj.balance;
19            return value;
20        }
21        return obj[prop];
22    }
23};
24const wrappedBankAcount = new Proxy(bankAccount, handler);
25console.log(wrappedBankAcount.dollars);
26console.log(wrappedBankAcount.dollars);
27console.log(wrappedBankAcount.dollars);
28console.log(wrappedBankAcount.dollars);
29// OUTPUT:
30// Calculating Dollars
31// 34.3008459
32// 34.3008459
33// 34.3008459
34// 34.3008459
35

正如你在例子中所看到的,我們有一個緩存對象,它保存着當前的銀行餘額和以美元爲單位的餘額價值。每次有人訪問 "dollar" 屬性時,我們都會先進行計算,然後將其緩存起來。

Dom 操作

我們想在每次餘額發生變化時更新屏幕上的文字。我們將使用一個 set 操作符 / trap,每次改變數值時,我們將更新屏幕上的 DOM 元素。

 1const bankAccount = {
 2  balance: 2020,
 3  name: "Georgy Glezer",
 4  get text() {
 5    return `${this.name} Balance Is: ${this.balance}`;
 6  }
 7};
 8const objectWithDom = (object, domId) => {
 9  const handler = {
10    set: function (obj, prop, value) {
11      obj[prop] = value;
12      document.getElementById(domId).innerHTML = obj.text;
13      return true;
14    }
15  };
16  return new Proxy(object, handler);
17};
18// create a dom element with id: bank-account
19const wrappedBankAccount = objectWithDom(bankAccount, "bank-account");
20wrappedBankAccount.balance = 26;
21wrappedBankAccount.balance = 100000;
22

在這裏,我們創建了一個輔助函數,這樣我們就可以存儲 DOM 元素的 ID,並在 set operator/trap 中添加了簡單的行來更新 DOM 元素。很簡單,對吧?讓我們看看結果:)

概要

綜上所述,我們瞭解了 ECMAScript 6 Proxies,我們如何使用它們,以及用於什麼目的。在我看來,代理是一個神奇的工具,你可以用它來做各種各樣的選擇,你只需要考慮什麼是最適合你的:) 。

資源

關於本文 譯者:飄飄 | 譯文來源:前端早讀課 作者:@Georgy Glezer 原文:https://levelup.gitconnected.com/the-amazing-power-of-javascript-proxies-aa27c6d06bcb

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/aaU31A6VVNgy3aFDbvOEdw