[譯]探索 ECMAScript 裝飾器

探索 ECMAScript 裝飾器

迭代器(Iterators), 生成器(generators)數組簡約式(array comprehensions);隨着時間的推移,JavaScript 和 Python 愈來愈像,現在我已經見怪不怪了。今天咱們就來討論一個相似 Python 語法的 ECMAScript 提議:裝飾器,該提案來自 Yehuda Katz。javascript

更新 07/29/2015: 裝飾器提議已經提交到TC39。最新進展你可在 提議 倉庫找到。如今又出了幾個 新的例子 (譯註:TC39 全稱 TC39 ECMA 技術委員會,受特許解決JavaScript語言相關事宜。)html

裝飾器模式

到底什麼是裝飾器?在 Python中,裝飾器提供了一個很是簡單的語法,用於調用高階函數。一個 Python 裝飾器就是一個函數,它包裝另一個函數來拓展功能,而不須要作顯式的修改。最簡單的 Python 裝飾器看起來是這樣的:前端

代碼頂部(@mydecorator)就是一個裝飾器,它看起來和 ES2016(ES7)沒有什麼區別,因此你必定分清楚!:)。java

@ 向編譯器代表,咱們正在使用裝飾器,mydecorator 指向一個同名的函數。咱們的裝飾器接受一個參數(被裝飾的函數),拓展功能後,返回一個與參數同名的函數。python

裝飾器幫助你添加任何你想拓展的功能,好比 memoization(譯者注:一種將函數返回值緩存起來的方法),強制訪問控制,身份驗證,插樁,時間函數,日誌,比率限制,等等。react

在 ES5 和 ES2015(即ES6) 中的裝飾器

在 ES5 中,實現命令式裝飾器(做爲純函數)是至關麻煩的。在 ES2015(即ES6)中,當類支持擴展,咱們有多個類須要共享一個功能時,咱們就須要更好的方法;或者說須要更好的分配方法。android

Yehuda 的裝飾器建議尋求在設計時對 JavaScript 類、屬性和對象字面量進行註釋和修改,同時保持聲明式語法。ios

讓咱們來看一些 ES2016 裝飾器吧!git

ES2016 裝飾器

想一想咱們在 Python 中學到的知識。一個 ES2016 裝飾器是一個表達式,它返回一個函數以及接收一個目標體,名稱,屬性描述符來做爲參數。經過在裝飾器前面加一個 @ 符號,而後放到被裝飾者的最上面來使用裝飾器。裝飾器能夠被定義爲類或者屬性。es6

裝飾一個屬性

咱們來看一個基礎的 Cat 類:

編譯這個類的結果就是將 meow 函數加載到 Cat.prototype,大體以下:

設想一下,咱們但願標記一個屬性或者方法名不能被編輯。裝飾器優先級高於定義屬性的語法,所以咱們能夠定義一個 @readonly 裝飾器,以下:

咱們就能夠這樣來定義 meow 屬性了,以下:

裝飾器就是一個表達式,它會被執行而後返回一個函數。這就是爲何 @readonly@something(parameter) 都能工做。

在 描述符(descriptor)加載進 Cat.prototype 以前,JavaScript引擎會先調用裝飾器:

如今 meow 變成了只讀,咱們能夠來驗證一下:

不只僅是屬性,接下來咱們來探討裝飾器類,在此以前咱們先來看第三方庫,儘管都很年輕,裝飾器庫從2016年開始陸續出現,包括由 Jay Phelps 開發的 decorators.js

和咱們上面實現的 readonly 同樣,decorators.js 包含了 @readonly , 只須要導入就好了:

它還包含其餘的裝飾器,好比 @deprecate ,主要是用於,當你的API須要提示方法可能會改變:

調用 console.warn() 打印描述信息。也能夠自定義描述信息,也能夠在描述信息中添加連接,以便進一步閱讀。

裝飾一個類

接下來咱們來看看裝飾類。根據提議規範,一個裝飾器接收構造函數做爲參數。假設有一個 MySuperHero 類,咱們能夠定義一個簡單的裝飾器 @superhero來裝飾它:

這能夠進一步拓展,經過提供參數使咱們可讓裝飾器定義成工廠函數:

ES2016 裝飾器做用於描述符和類。它們會自動接收被傳遞的屬性名和目標對象,咱們很快會講到。經過對描述符的訪問,裝飾器能夠作到更改屬性使其使用 getter,或開啓一些本來很是繁瑣的操做,好比在第一次訪問屬性時自動綁定方法到當前實例。

ES2016 裝飾器 和 Mixins 模式

我拜讀了 Reg Braithwaite 最近的文章 ES2016 Decorators as mixins 和以前的一篇 Functional Mixins。Reg 提出使用一個 helper 將不一樣行爲混入任意一個目標(類原型或者 standalone),並表述爲一個類專屬的版本。這種功能性的混入會把實例行爲混入類原型,使其看起來像這樣:

好了,咱們如今能夠定義一些 mixins ,而後嘗試用它們裝飾一個類。假設咱們有一個簡單的 ComicBookCharacter 類:

ComicBookCharacter 多是世界上最無聊的角色了,可是咱們能夠定義一些 mixins,爲它提供超能力(SuperPowers)和 裝備(UtilityBelt),讓咱們用 Reg 的 mixin helper 來實現吧:

如今咱們就能夠經過在 mix 函數前面加 @ 的語法,根據咱們想要的屬性來裝飾 ComicBookCharacter。注意咱們是如何在類上面加多個裝飾器語句的:

如今咱們能夠塑造一個蝙蝠俠角色了。

這些類的裝飾器相對緊湊,我能夠將它們用做函數調用的替代方法,或者做爲高階組件的助手。

注: @WebReflection 有一些替代方案,用於本節中使用的mixin模式,您能夠點擊 瞭解更多

經過 Babel 使用裝飾器

裝飾器(在我寫本文的時候)仍然仍是一個提案。他們尚未經過。感謝 Babel 支持在實驗模式使用裝飾器語法,因此本文的大部分例子均可以直接使用。

若是你使用 Babel CLI,你能夠出入以下參數:

$ babel --optional es7.decorators
複製代碼

或者直接調用 transformer:

這裏有一個 Babel 在線的 REPL;複選框選中「Experimental」就可使用裝飾器了。

有趣的實驗

我很幸運坐在 Paul Lewis 的旁邊,他在嘗試用裝飾器從新調度讀寫 DOM 的代碼。它借鑑了 Wilson Page 的 FastDOM,可是提供了更精簡的API。Paul 的 read/write 裝飾器能夠經過 console 來提醒你,若是你在改變佈局時使用 @write 後調用方法或者屬性(或者使用 @read 後改變DOM)。

下面是 Paul 的一個實驗例子,在使用 @read 後嘗試改變 DOM,會在 console 中打印異常:

如今就去試試裝飾器吧!

在短期來看,ES2016裝飾器對於聲明式裝飾和註釋,類型檢查和在 ES2015 類中應用裝飾器都是大有裨益的,從長遠來看,他們能夠提供很是有用的靜態分析工具(編譯時類型檢查和自動補全)。

他們和經典面嚮對象語言(OOP)中的裝飾器沒有區別,容許一個對象能夠被行爲裝飾,不論是動態的仍是靜態的都不會影響到來自同一個類的對象。裝飾器的提案還一直在變化中,讓咱們持續關注 Yehuda 的提案吧。

第三方庫的做者正在討論,裝飾器可能會替換 mixins,以及他們能夠應用到 React 的高階組件中。

我我的很但願看到關於裝飾器愈來愈多的嘗試,你能夠在 Babel 上嘗試,識別出可複用的裝飾器,也許你也能夠像 Paul 那樣分享你的做品 :)

瞭解更多以及參考

感謝 Jay PhelpsSebastian McKenziePaul LewisSurma 對本文的審校以及提供的詳細反饋❤


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索