JavaScript ES6代理的實際用例

0_ZFkP9UMWOHyl8_zg.jpeg

元編程是一種強大的技術,使你可以編寫能夠建立其餘程序的程序。ES6藉助代理和許多相似功能,使在JavaScript中利用元編程變得更加容易。ES6 Proxy(代理) 有助於從新定義對象的基本操做,從而爲各類可能性打開了大門。javascript

本指南能夠幫助您理解爲何ES6代理如此之好,尤爲是對於元編程而言:html

  • 什麼是ES6代理
  • 如何以及什麼時候實施代理
  • 如何使用ES6代理執行訪問控制,數據綁定和緩存
  • ES6代理不是性能密集型任務的理想選擇

先決條件和結果

本教程主要針對有JavaScript經驗的開發人員,至少要熟悉ES6代理的概念。若是你已經對代理做爲一種設計模式有了牢固的理解,那麼這些知識應該能夠轉化爲現實。前端

閱讀本指南後,你應該可以:java

  • 瞭解什麼是ES6代理,如何實現以及什麼時候使用它
  • 使用ES6代理進行訪問控制,緩存和數據綁定

ES6代理剖析:目標(Target),處理程序(handler)和陷阱(trap)

從根本上來講,代理是指某件事或某人成爲其餘事物的替代品,因此不論是什麼東西,都要通過替代品才能達到真正的交易。ES6代理的工做原理也是如此。es6

爲了有效地實現和使用ES6代理,你必須瞭解三個關鍵術語:編程

  1. Target——代理人所替代的真正的交易,目標是站在代理背後的東西。這能夠是任何對象。
  2. Handler——一個包含全部代理的陷阱邏輯的對象。
  3. Trap——與操做系統中的陷阱相似,此上下文中的陷阱是以某種方式提供對對象的訪問的方法(可理解爲攔截行爲)。

綜上所述,下面是最簡單的實現,若是使用ES6代理,對象中不存在給定的屬性,則能夠返回不一樣的內容。設計模式

const target = {
  someProp: 1
}

const handler = {
  get: function(target, key) {
    return key in target ? 
      target[key] : 
    'Doesn't exist!';
  }
}

const proxy = new Proxy(target, handler);
console.log(proxy.someProp) // 1
console.log(proxy.someOtherProp) // Doesn't exist!

ES6代理是一項強大的功能,可促進JavaScript中對象的虛擬化。api

數據綁定:同步多個對象

因爲數據綁定的複雜性,它一般很難實現。ES6代理實現雙向數據綁定的應用能夠在JavaScript的MVC庫中看到,在這些庫中,當DOM發生變化時,對象會被修改。緩存

簡而言之,數據綁定是一種將多個數據源綁定在一塊兒以使其同步的技術。微信

假設存在一個名爲 username<input>

<input type="text" id="username" />

假設你要使此輸入的值與對象的屬性保持同步。

const inputState = {
  id: 'username',
  value: ''
}

當輸入的值發生變化時,經過監聽輸入的變化事件,而後更新 inputState 的值,修改 inputState 是至關容易的。然而,反過來,在 inputState 被修改時更新輸入,則至關困難。

ES6代理能夠在這種狀況下提供幫助。

const input = document.querySelector('#username')
const handler = {
  set: function(target, key, value) {
    if (target.id && key === 'username') {
      target[key] = value;
      document.querySelector(`#${target.id}`)
        .value = value;
      return true
    }
    return false
  }
}

const proxy = new Proxy(inputState, handler)
proxy.value = 'John Doe'
console.log(proxy.value, input.value) 
// 雙方都將印有「 John Doe」

這樣,當 inputState 更改時,input 將反映已進行的更改。結合偵聽 change 事件,這將生成 inputinputState 的簡單雙向數據綁定。

雖然這是一個有效的用例,但一般不建議這樣作。之後再說。

緩存:提升代碼性能

緩存是一個古老的概念,它容許很是複雜和大型的應用程序保持相對的性能。緩存是存儲某些數據的過程,以便在請求時能夠更快地提供數據。緩存並不永久地存儲任何數據。緩存失效是保證緩存新鮮的過程。這是開發人員共同的苦惱。正如Phil Karlton所說:"計算機科學中只有兩件難事:緩存無效和給事物命名。"

ES6代理使緩存更加容易。例如,若是你要檢查對象中是否存在某些東西,它將首先檢查緩存並返回數據,或者若是不存在則進行其餘操做以獲取該數據。

假設你須要進行不少API調用才能獲取特定信息並對其進行處理。

const getScoreboad = (player) => {
  fetch('some-api-url')
    .then((scoreboard) => {
    // 用記分牌作點什麼
  })
}

這就意味着,每當須要一個球員的記分牌時,就必須進行一次新的調用。相反,你能夠在第一次請求時緩存記分牌,隨後的請求能夠從緩存中獲取。

const cache = { 
  'John': ['55', '99']
}
const handler = { 
  get: function(target, player) {
    if(target[player] {
       return target[player]
  } else {
    fetch('some-api-url')
      .then(scoreboard => {
        target[player] = scoreboard
        return scoreboard
      })
    }
  }
}
const proxy = new Proxy(cache, handler)
// 訪問緩存並使用記分牌作一些事情

這樣,僅當緩存中不包含玩家的記分牌時,纔會進行API調用。

訪問控制:控制進出對象的內容

最簡單的用例是訪問控制,ES6代理的大部份內容都屬於訪問控制。

讓咱們探索使用E6代理的訪問控制的一些實際應用。

1. 驗證

ES6代理最直觀的用例之一是驗證對象內部的內容,以確保對象中的數據儘量準確。例如,若是你想強制執行產品描述的最大字符數,能夠這樣作。

const productDescs = {}
const handler = {
  set: function(target, key, value) {
    if(value.length > 150) {
      value = value.substring(0, 150)
    }
    target[key] = value
  }
}
const proxy = new Proxy(productDescs, handler)

如今,即便你添加的描述超過150個字符,也會被刪減並添加。

2. 提供對象的只讀視圖

有時候你可能要確保不以任何方式修改對象,而且只能將其用於讀取目的。 JavaScript提供了 Object.freeze() 來執行此操做,可是使用代理時,該行爲更可自定義。

const importantData = {
  name: 'John Doe',
  age: 42
}

const handler = {
  set: 'Read-Only',
  defineProperty: 'Read-Only',
  deleteProperty: 'Read-Only',
  preventExtensions: 'Read-Only',
  setPrototypeOf: 'Read-Only'
}

const proxy = new Proxy(importantData, handler)

如今,當你嘗試以任何方式更改對象時,你只會收到一個字符串,表示只讀。不然,你可能會引起錯誤以指示該對象是隻讀的。

3. 私有屬性

JavaScript自己並無私有屬性,除了閉包。當 Symbol 數據類型被引入時,它被用來模仿私有屬性。但隨着Object.getOwnPropertySymbols 方法的引入,它被拋棄了。ES6代理並非一個完美的解決方案,但在緊要關頭它們能夠完成任務。

一種常見的約定是經過在名稱前加上下劃線來標識私有屬性,這個約定容許你使用ES6代理。

const object = {
  _privateProp: 42
}

const handler = {
  has: function(target, key) {
    return !(key.startsWith('_') && key in target)
  },
  get: function(target, key, receiver) {
    return key in receiver ? target[key] : undefined
  }
}

const proxy = new Proxy(object, handler)
proxy._privateProp // undefined

添加 ownKeysdeleteProperty 會讓這個實現更接近於真正的私有屬性。而後,你仍然能夠在開發者控制檯中查看代理對象。若是你的用例與上面的實現一致,它仍然適用。

爲什麼以及什麼時候使用代理

ES6代理並非性能密集型任務的理想選擇。這就是爲何進行必要的測試是相當重要的。代理能夠在任何預期對象的地方使用,代理只需幾行代碼就能提供複雜的功能,這使它成爲元編程的理想功能。

代理一般與另外一個稱爲Reflect的元編程功能一塊兒使用。


來源:https://blog.logrocket.com,做者:Eslam Hefnawy Follow,翻譯:公衆號《前端全棧開發者》

本文首發於微信公衆號《前端全棧開發者》,關注即送大禮包,準能爲你節省很多錢!
subscribe.png

相關文章
相關標籤/搜索