JavaScript代理的驚人威力

今天咱們要學習的是ECMAScript 6的代理。咱們將在本文中涉及如下主題。vue

  • 什麼是代理
  • 代理人在行動
  • 誰使用代理
  • 使用案例和實例
  • 資源

讓咱們開始吧:)git

什麼是代理

MDN網站所述。github

Proxy對象使你可以爲另外一個對象建立一個代理,它能夠攔截和從新定義該對象的基本操做。

他們在解釋什麼是代理的時候,說它能夠建立一個代理**,這有點搞笑。固然,他們並無錯,但咱們能夠簡化這個說法,使其更加友好,說設計模式

_Proxy_對象可讓你包裹目標對象,經過這樣作,咱們能夠攔截和從新定義該對象的基本操做。

基本上,它的意思是咱們要把一個對象,用Proxy包裹起來,這將容許咱們建立一個 "隱藏 "的門,並控制全部對所需對象的訪問。瀏覽器

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

const target = {
  message1: "hello",
  message2: "everyone"
};

const handler = {};

const proxy = new Proxy(target, handler);

一個 "代理 "是用兩個參數建立的。

  • "target":你要包裹的原始對象(代理)。
  • handler:定義哪些操做將被攔截,以及如何從新定義被攔截的操做的對象,也能夠調用 "陷阱"。

大多數瀏覽器都支持代理,但也有一些老的瀏覽器不支持(固然是IE),你能夠查看完整的列表這裏。google有一個polyfill的Proxies,但它不支持全部的Proxy功能。服務器

如今咱們知道了什麼是代理,咱們想看看咱們能用它作什麼。app

代理人在行動

讓咱們想象一下,咱們是A Bank。咱們想知道每次銀行帳戶餘額被訪問並被通知上。咱們將使用最簡單的handler操做/陷阱。getdom

在上面的例子中,咱們有一個銀行帳戶對象,裏面有個人名字和2020的餘額。函數

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

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

  • target。被訪問的對象(咱們封裝的對象)。
  • prop: 被訪問的屬性。在咱們的例子中被訪問的屬性--這裏是 "balance"。
  • receiver:接受者。能夠是代理,也能夠是繼承自代理的對象。

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

從輸出中能夠看到,一旦 "餘額 "屬性被訪問,咱們經過使用代理和設置get操做/陷阱,很容易就通知(log)了此次訪問。

咱們繼續用咱們Bank的想法,要求每次有人從銀行帳戶中取錢,咱們都要通知一下。而另外一個約束條件是,銀行不容許出現負餘額。爲了達到這個目的,咱們此次要使用set處理程序/陷阱。

在上面的例子中,咱們通知當前的餘額和取款後的新餘額,若是新的餘額爲負數,咱們也會通知並停止取款操做。

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

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

  • target: 被訪問的對象(咱們封裝的對象)。
  • prop:正在訪問的對象(咱們封裝的對象)。在咱們的例子中,被訪問的屬性是 "balance"。
  • value。應該更新的新值。
  • receiver*: 原先被分配到的對象。這一般是代理自己。但set()處理程序也能夠經過原型鏈或其餘各類方式間接調用。

你能夠看到,它其實和get很類似,但只是多接收了1個新值的參數。

這2個操做符/陷阱是最多見的,若是你有興趣找到全部現有的操做符/陷阱,你能夠查看這裏

誰使用代理

許多流行的庫都使用了這種技術,例如: * [MobX]()

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

使用案例和示例

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

  • 記錄(通知銀行)
  • 驗證(阻止負數餘額更新)

緩存

咱們將再次使用get操做符/陷阱,並將 "dollars "屬性添加到咱們的對象中。在每次訪問 "dollars "屬性時,咱們將計算咱們的餘額價值多少美圓。由於計算是一個繁重的操做,因此咱們但願儘量的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

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

DOM操做

咱們想在每次餘額發生變化時更新屏幕上的文字。咱們將使用一個set操做符/陷阱,在每次改變數值時,咱們將更新屏幕上的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;

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

Image for post

概要

綜上所述,咱們瞭解了ECMAScript 6 Proxies,咱們如何使用它們,以及用於什麼目的。在我看來,Proxies是一個很神奇的工具,你能夠用它來作各類各樣的選擇,你只須要想一想什麼最適合你:) 。

若是您喜歡這篇文章,歡迎關注並拍手👏。

資源簡介

相關文章
相關標籤/搜索