JS 開發者必須知道的十個 ES6 新特性

吳鵬煜發表於2016-07-25

最近我參加了一個在舊金山舉行的HTML5開發者大會,聽到的演講半數都關於ES6(或者官方說法叫ECMAScript2015),我喜歡簡潔的成為ES6。

這篇文章會給你簡單介紹一下ES6。如果你還不知道什麼是ES6的話,它是JavaScript一個新的實現,如果你是一個忙碌的JavaScript開發者(但誰不是呢),那麼繼續讀下去吧,看看當今最熱門的語言——JavaScript的新一代實現中,最棒的十個特性。

這是為忙碌的開發者準備的ES6中最棒的十個特性(無特定順序):

  1. 預設引數
  2. 模版表示式
  3. 多行字串
  4. 拆包表示式
  5. 改進的物件表示式
  6. 箭頭函式 =&>
  7. Promise
  8. 塊級作用域的letconst
  9. 模組化

注意:這個列表十分主觀並且帶有偏見,其他未上榜的特性並不是因為沒有作用,我這麼做只是單純的希望將這份列表中的專案保持在十個。

首先,一個簡單的JavaScript時間線,不瞭解歷史的人也無法創造歷史。

  1. 1995年:JavaScript以LiveScript之名誕生
  2. 1997年:ECMAScript標準確立
  3. 1999年:ES3釋出,IE5非常生氣
  4. 2000年-2005年:XMLHttpRequest,熟知為AJAX,在如Outlook Web Access(2002)、Oddpost(2002)、Gmail(2004)、Google Maps(2005)中得到了廣泛的應用
  5. 2009年:ES5釋出(這是我們目前用的最多的版本),帶來了forEach / Object.keys / Object.create(特地為Douglas Crockford所造,JSON標準建立者) ,還有JSON標準。

歷史課上完了,我們回來講程式設計。

1. ES6中的預設引數

還記得我們以前要這樣子來定義預設引數:

這樣做一直都沒什麼問題,直到引數的值為0,因為0在JavaScript中算是false值,它會直接變成後面硬編碼的值而不是0本身。當然了,誰要用0來傳值啊(諷刺臉)?所以我們也忽略了這個瑕疵,沿用了這個邏輯,否則的話只能…..沒有否則!在ES6中,我們可以把這些預設值直接放在函式簽名中。

對了,這個語法和Ruby很像!

2. ES6中的模版表示式

模版表示式在其他語言中一般是為了在模版字串中輸出變數,所以在ES5中,我們非得把字串破開變成這樣:

幸運的是在ES6中我們有了新語法,在反引號包裹的字串中,使用${NAME}語法來表示模板字元:

3. ES6中的多行字串

另一個好吃的語法糖就是多行字串,以前我們的實現是像這樣的:

但是在ES6中,只要充分利用反引號。

4. ES6中的拆包表示式

拆包可能是一個比較難理解的概念,因為這裡面真的是有魔法發生。假如說你有一個簡單的賦值表示式,把物件中的housemouse賦值為housemouse的變數。

另一個拆包的例項(Node.js):

但是在ES6中我們可以用以下語句替換:

甚至在陣列中也能用,簡直瘋狂!

習慣拆包語法可能需要一些時間,但是這絕對是糖衣炮彈。

5. ES6中改進的物件表示式

你能用物件表示式所做的是超乎想象的!類定義的方法從ES5中一個美化版的JSON,進化到ES6中更像類的構造。

這是一個ES5中典型的物件表示式,定義了一些方法和屬性。

如果你想做的好看一點,我們可以用Object.create方法來讓 serviceBase 成為 accountServiceES5 的 prototype 從而實現繼承。

我知道 accountServiceES5ObjectCreate 和 accountServiceES5 不完全相同的。因為一個物件 accountServiceES5 會有如下所示的 __proto__ 屬性:
JS 開發者必須知道的十個 ES6 新特性

但對於這個示例,我們就把這兩者考慮為相同的。所以在ES6的物件表示式中,我們把getAccounts: getAccounts簡化為getAccounts,,並且我們還可以用__proto__直接設定prototype,這樣聽起來合理的多。(不過並不是用proto

還有,我們可以呼叫 super 和動態索引(valueOf_1_2_3)

JS 開發者必須知道的十個 ES6 新特性

這是對老舊的物件表示式一個很大的改進!

6. ES6中的箭頭函式

這或許是我最想要的一個特性,我愛 CoffeeScript 就是因為他胖胖的箭頭(=&>相對於-&>),現在ES6中也有了。這些箭頭最神奇的地方在於他會讓你寫正確的程式碼。比如,this在上下文和函式中的值應當是相同的,它不會變化,通常變化的原因都是因為你建立了閉包。

使用箭頭函式可以讓我們不再用that = this或者self = this或者_this = this或者.bind(this)這樣的程式碼,比如,這些程式碼在ES5中就特別醜。

這是在ES6中去掉_this = this之後:

可惜的是,ES6委員會覺得再加上瘦箭頭(-&>)的話就對我們太好了,所以他們留下了一個老舊的function。(瘦箭頭在CoffeeScript中的作用就像ES5/6中一樣)

在ES6中我們無需_this

注意,在ES6中你可以合理的把箭頭函式和舊式 function 函式混用。當箭頭函式所在語句只有一行時,它就會變成一個表示式,它會直接返回這個語句的值。但是如果你有多行語句,你就要明確的使用return

這是ES5中利用messages陣列建立一個陣列的程式碼:

在ES6中會變成這樣:

注意到我用了字串模版嗎,又一個從CoffeeScript中來的功能,我愛它們!

在只有一個引數的函式簽名中,括號是可有可無的,但是如果多於一個引數時就要加上。

7. ES6中的Promise

Promise是一個有爭議的話題。現在有很多Promise實現,語法也大致相同,比如q/ bluebird/ deferred.js/ vow/ avow/ jquery deferred等等。其他人說我們並不需要Promise,非同步,回撥和generator之類的就很好。慶幸的是,現在在ES6中終於有一個標準的Promise實現。

我們來看一個相當微不足道的延遲非同步執行,用setTimeout實現

我們可以用ES6中的Promise重寫:

或者用ES6的箭頭函式:

到現在為止,我們只是單純增加了程式碼的行數,還明顯沒有帶來任何好處,你說的對。但是如果我們有更多複雜的邏輯內嵌在setTimeout()中的回撥時好處就來了:

可以用ES6中的Promise重寫:

還是無法相信Promise比普通回撥要好?我也不信。我想一旦知道了回撥這個方法它就會在你腦中縈繞,額外的複雜的Promise也沒有必要存在了。

不論怎麼說,ES6中的Promise是為會欣賞的人準備的,Promise有一個不錯的失敗-捕捉回撥機制,看看這篇文章吧,裡面有更多關於Promise的資訊。ES6 Promise介紹

8. 塊級作用域的letconst

你可能早就聽過對ES6中的let那些奇怪的傳說,我記得我第一次到倫敦時為那些TO LET牌子感到非常困惑。但是ES6中的let和出租無關,這不算是語法糖,它很複雜。let是一個更新的var,可以讓你把變數作用域限制在當前塊裡。我們用{}來定義塊,但是在ES5中這些花括號起不到任何作用。

執行結果將會是1000。天啊!這是多大的一個Bug。在ES6中,我們用let來限制變數作用域為函式內。

執行結果是0,因為在if塊中也有let。如果什麼都沒有的話(amount=1),那麼結果將會是1

說到const,事情就簡單多了。他僅僅產生是一個不可變的變數,並且他的作用域也像let一樣只有塊級。為了演示,這裡有定義了一堆常量,並且由於作用域的原因,這些定義都是有效的。

依我愚見,letconst讓這門語言變得更加複雜,沒有這些的時候我們只有一條路可以走,但是現在可以要考慮更多的情景。;-(

9. ES6中的類

如果你喜歡物件導向程式設計,那麼你會特別喜歡這個特性。他讓你編寫和繼承類時就跟在Facebook上發一個評論這麼簡單。

在ES5中,因為沒有class關鍵字(但它是毫無作用的保留字),類的建立和使用是讓人十分痛苦的事情。更慘的是,很多偽類的實現像pseude-classical, classical, functional讓人越來越摸不著頭腦,為JavaScript的信仰戰爭火上澆油。

我不會給你展示在ES5中怎麼去編寫一個類(是啦是啦從物件可以衍生出來其他的類和物件),因為有太多方法去完成。我們直接看ES6的示例,告訴你ES6的類會用prototype來實現而不是function。現在有一個baseModel類,其中我們可以定義建構函式和getName()方法。

注意到我給optionsdata用了預設引數,而且方法名再也不用加上function或者:了。還有一個很大的區別,你不能像建構函式裡面一樣向this.Name指派值。怎麼說呢,和函式有相同縮排的程式碼裡,你不能向name賦值。如果有這個需要的話,在建構函式裡面完成。

使用NAME extends PARENT_NAME語法,AccountModelbaseModel繼承而來。

呼叫父類建構函式時,只需帶上引數輕鬆的呼叫super()方法。

想要高階一點的話,你可以像這樣弄一個getter方法,這樣accountsData就會變成一個屬性。

現在你要怎麼用這個魔咒,很簡單,就跟讓三歲小孩相信聖誕老人存在一樣。

如果好奇輸出結果的話:

10. ES6中的模組化

你可能知道,ES6之前JavaScript並沒有對模組化有過原生的支援,人們想出來AMDRequireJSCommenJS等等,現在終於有importexport運算子來實現了。

ES5中你會用script標籤和IIFE(立即執行函式),或者是其他的像AMD之類的庫,但是ES6中你可以用export來暴露你的類。我是喜歡Node.js的人,所以我用和Node.js語法一樣的CommonJS,然後用Browserfy來瀏覽器化。現在我們有一個port變數和getAccounts方法,在ES5中:

在ES5的main.js中,用require('模組')來匯入:

但是在ES6中,我們用exportimport。比如這是ES6中的module.js檔案:

在需要引入的main.js檔案中,可以用import {名稱} from '模組'語法:

或者就直接在main.js中引入所有的變數:

個人來說,我覺得這樣的模組化有些搞不懂。確實,這樣會更傳神一些 。但是Node.js中的模組不會馬上就改過來,瀏覽器和伺服器的程式碼最好是用同樣的標準,所以目前我還是會堅持CommonJS/Node.js的方式。

目前來說瀏覽器對ES6的支援還遙遙無期(本文寫作時),所以你需要一些像jspm這樣的工具來用ES6的模組。

想要了解更多ES6中的模組化和例子的話,來看這篇文章,不管怎麼說,寫現代化的JavaScript吧!

怎麼樣可以在今天就用上ES6(Babel)

ES6標準已經敲定,但還未被所有瀏覽器支援(Firefox的ES6功能一覽),如果想馬上就用上ES6,需要一個像Babel這樣的編譯器。你可以把他當獨立工具用,也可以將他整合到構建系統裡,Babel對GulpGruntWebpack都有對應的外掛

JS 開發者必須知道的十個 ES6 新特性

安裝Gulp外掛示例:

gulpfile.js中,定義這麼一個任務,將src目錄下的app.js檔案編譯到build目錄下:

Node.js和ES6

對於Node.js,你可以用構建工具或者直接用獨立模組babel-core

然後在Node.js中呼叫這個函式:

ES6的一些總結

ES6中還有很多你可能都用不上(至少現在用不上)的可圈可點的特性,以下無特定順序:

  1. Math / Number / String / Array / Object中新的方法
  2. 二進位制和八進位制資料型別
  3. 自動展開多餘引數
  4. For of迴圈(又見面了CoffeeScript)
  5. Symbols
  6. 尾部呼叫優化
  7. generator
  8. 更新的資料結構(如MapSet

和有些人吃了一片薯片就一發不可收拾的人一樣(再來一片嘛就一片),對於那些停不下來想要知道關於更多ES6相關資訊的成績優秀的同學,我準備了擴充套件閱讀:

  1. ES6速查手冊PDF
  2. Nicholas C. Zakas所著的理解ECMAScript6
  3. Dr. Axel Rauschmayer所著的探索ECMAScript6

相關文章