每一個 JavaScript 開發者都該瞭解的 ES2018 新特性

原文做者:Faraz Kelhinijavascript

譯者:UC 國際研發 Jothycss


寫在最前:歡迎你來到「UC國際技術」公衆號,咱們將爲你們提供與客戶端、服務端、算法、測試、數據、前端等相關的高質量技術文章,不限於原創與翻譯。前端

編者按:曾幾什麼時候,年少的我捧着阮一峯老師的《ES6 標準入門》,感嘆 JS 變遷實在太快,好怕學不動了。直至寫了幾年 ES6 的今日,回頭看方知:不要爲了學 ES X 而學 ES X,不管 ES 幾其實都是語法糖,是輔助角色,重點是想清楚它能爲咱們的開發帶來什麼好處,而不是本末倒置。今天介紹的 ES2018 新特性仍是有蠻多亮點的,一塊兒來看看吧。java


ECMAScript 標準的第九版,官宣爲 ECMAScript 2018(或簡稱 ES2018),已於 2018 年 6 月發佈。從 ES2016 開始,每隔一年就會發布一版 ECMAScript 規範的新版本,並添加很少於主版本的功能。 最新的這個版本延續了每一年發佈的週期,新增了四個新的 RegExp 特性,rest/spread 屬性,異步迭代和 Promise.prototype.finally。 此外,它還從標記模板中刪除了轉義序列的語法限制。
git

咱們將在後面的小節中詳細解釋這些新變化 😉。程序員



rest/spread 屬性

回顧 ES2015,最有趣的功能當數 spread 運算符。 該運算符極大簡化了數組的複製及合併。你可使用它替換concat()slice()方法:github


在必須將數組的各個項分別做爲參數傳入函數的狀況下,擴展運算符也派得上用場。 例如:正則表達式


經過向對象文法添加 spread 屬性,ES2018 進一步擴展了此語法。 使用 spread 屬性,你能夠將對象自身的可枚舉屬性複製到新對象上。舉個例子 🌰:算法

在此代碼中,... 運算符用於檢索 obj1 的屬性並將它們分配給 obj2。 在 ES2018 以前,這麼作會報錯的。 若是出現多個屬性同名的狀況,將會取最後一個值:編程


Spread 屬性還提供了一種合併兩個或多個對象的新方法,能夠替代 Object.assign() 方法使用:

在此代碼中,Object.assign() 方法會執行其繼承的 setter 屬性,而 spread 屬性則徹底忽略了這一步。

切記!spread 屬性只複製可枚舉屬性。 在下面的例子中,type 屬性不會出如今複製出的對象中,由於其enumerable 屬性爲 false

繼承屬性即便是可枚舉的,也會被忽略:

在這段代碼中,car2 繼承了carcolor 屬性。 因爲 spread 僅複製對象自身的屬性,所以返回值中不包含 color 屬性。

請記住,spread 只是對象的淺複製。 若是屬性中包含對象,則僅複製對象的引用:

copy1 中的 x 屬性與copy2中的x 屬性引用了內存中同一個對象,所以嚴格等於(strict equality)運算符返回 true.

ES2015 新增的另外一有用功能是 rest 參數,它使 JavaScript程序員可以使用...將值表示爲數組。 例如:

arr 的第一項被賦值給 x,剩下的被賦值給 rest 變量。 這種名爲數組解構的模式很是受歡迎,以致於 Ecma 技術委員會(Ecma Technical Committee)決定爲對象帶來相似的功能:

此代碼使用 rest 屬性解構賦值,將對象 obj 剩餘的自身可枚舉屬性複製到新對象rest中。 須要引發注意的是,rest 屬性必須始終位於對象的末尾,不然會報錯:

此外,在對象中使用多個 rest 語法也會報錯,除非它們是嵌套使用的:


Rest/Spread 屬性支持

Node.js:
  • 8.0.0(須要 --harmony 運行時 flag)

  • 8.3.0(徹底支持)


異步迭代

迭代數據集是編程的重要組成部分。 在 ES2015 以前,JavaScript提供了for,for...inwhile 等語句,以及map(),filter()forEach() 等方法。 爲了方便程序員一個個地處理集合元素,ES2015 引入了迭代器接口。

若是對象具備 Symbol.iterator 屬性,則表示它是可迭代的。 在 ES2015 中,字符串和集合對象(如Set, MapArray)帶有Symbol.iterator 屬性,所以是可迭代的。 如下代碼說明了如何每次訪問一個可迭代元素☝️:

Symbol.iterator 是個廣爲人知的符號,用於表示返回迭代器的函數。與迭代器交互主要使用next()方法。此方法返回一個具備valuedone 兩個屬性的對象。 value 屬性包含集合中下一個元素的值。done 屬性包含truefalse,代表是否已到達集合的末尾。

默認狀況下普通對象不可迭代,但若是在其上定義了 Symbol.iterator 屬性,則它可變爲可迭代對象,以下所示:

collection 對象是可迭代的,由於它定義了 Symbol.iterator 屬性。 iterator 使用 Object.keys() 方法獲取對象屬性名的數組,而後將其賦值給常量values.它還定義了一個計數器變量i,初始值爲 0. 當執行迭代器時,它返回一個包含 next() 方法的對象。 每次調用 next() 方法時,它都返回一個 {value, done} 鍵值對,其中 value 保存集合中的下一個元素,done 保存一個布爾值,表示迭代器是否已達到集合的末尾。

雖然以上代碼運行完美,但它本無需如此複雜。 所幸,生成器( generator )函數能夠大大簡化該過程:

在今生成器中,for...in 循環用於枚舉集合,yield 每一個屬性的值。 結果與前一個示例徹底相同,但代碼量大大減小。

迭代器的缺點是它們不適合表示異步數據源。 ES2018 的補救方案是異步迭代器(asynchronous iterators)和異步可迭代對象(asynchronous iterables)。 異步迭代器與傳統迭代器的不一樣點在於,它不返回 {value,done} 的形式的普通對象,而是返回一個完成(fulfill) {value,done}promise.異步可迭代對象定義了一個返回異步迭代器的 Symbol.asyncIterator 方法(注意不是 Symbol.iterator)。

舉個例子🌰可能更清楚些:

請注意,使用 promises 的迭代器不可能達到相同的結果。 雖然普通的同步迭代器能夠異步產生肯定值,但它仍然須要同步肯定「完成(done)」的狀態。


一樣,你可使用生成器函數簡化此過程,以下所示:

一般生成器函數會返回帶有next()方法的生成器對象。 當調用 next() 時,它返回一個 {value,done} 鍵值對,其 value 屬性保存了yield 的值。 異步生成器與之相似,只不過它返回的是一個完成了 {value,done} 的promise.

使用 for...of 語句能夠輕鬆迭代可迭代對象,可是 for...of 不能與異步可迭代對象一塊兒使用,由於 valuedone 不是同步產生的。 出於這個緣由,ES2018 提供了 for...await...of 語句。 咱們來看一個例子:

在此代碼中, for...await...of 語句隱式調用集合對象上的Symbol.asyncIterator方法以獲取異步迭代器。 每次循環時,都會調用迭代器的 next() 方法,該方法返回一個 promise. 一旦 promise 完成,就會將結果對象的 value 屬性讀取到x變量。 循環繼續,直到返回對象的 done 屬性值爲true.

敲黑板!for...await...of 語句僅在異步生成器和異步函數中有效。 違反此規則會報 SyntaxError(語法錯誤)。


next() 方法可能會返回 rejected promise. 爲了優雅地處理被 reject 的 promise,你可使用try...catch語句包裹for...await...of 語句,以下所示:

異步迭代器支持

Node.js:

  • 8.10.0(須要 --harmony_async_iteration flag)

  • 10.0.0(徹底支持)


Promise.prototype.finally

ES2018另外一振奮人心的特效是finally()方法。 以前有幾個 JavaScript 庫實現了相似的方法,而且它被證明是有用的。這促使 Ecma技術委員會正式將finally()添加到規範中。 使用該方法,開發者可無需理會 promise 命數如何,直接執行這個代碼塊中的代碼。 咱們來看一個簡單的例子:

finally() 方法可在操做完成後進行一些掃尾(clean up)工做,不管操做是否成功。 在此代碼中,finally() 方法在數據獲取處理後直接隱藏了加載 spinner。 不管 promise 完成與否,函數中的註冊代碼都會執行,開發者沒必要在 then()catch() 方法中重複編寫邏輯。

使用promise.then(func, func)也可實現與promise.then(func, func) 一樣的效果,但你必須在 fulfillment 句柄及 rejection 句柄中重複相同的代碼,或者引入一個變量:


then()catch() 相同,finally() 方法老是返回一個 promise,所以你能夠連接更多的方法。 通常來講,咱們會將 finally() 做爲最後一環。但某些狀況,例如在建立 HTTP 請求時,在 finally() 以後連接另外一個catch(),以處理請求中可能發生的錯誤是不錯的實踐。

Promise.prototype.finally 支持

Node.js:

10.0.0(徹底支持)


RegExp 新特性

ES2018 爲 RegExp 對象增長了四個新特性,進一步提升了 JavaScript 的字符串處理能力。 這些特性以下:

  • s(dotAll)標誌

  • 可命名捕獲組

  • Lookbehind斷言

  • Unicode 屬性轉義

s (dotAll) Flag

點(.)是正則表達式模式中的特殊字符,它匹配除換行符以外的任何字符,例如換行符(\n)或回車符(\r)。要匹配包括換行符在內的全部字符,解決方法是使用兩個相反短字的字符類,例如[\d\D]. 此字符類告訴正則表達式引擎找到一個數字(\d)或非數字(\D)的字符。 所以,它匹配任意字符:

ES2018 引入了一種模式,其中點可用於實現相同的結果。可使用s標誌在每一個正則表達式的基礎上激活此模式:

利用標誌來選擇性使用新特性的好處是向後兼容,保證使用點字符的現有正則表達式模式不受影響。

可命名捕獲組

在一些正則表達式模式中,使用數字來引用捕獲組可能會形成混淆。 例如,採用正則表達式 /(\d{4})-(\d{2})-(\d{2})/ 匹配日期。 因爲美式英語中的日期符號與英式英語不一樣,所以很難知道哪一個組指的是日,哪一個組指的是月:

ES2018 引入了使用(?<name>...)語法的命名捕獲組。 所以,匹配日期的模式能夠用不太模糊的方式編寫:

你可使用 \k<name> 語法在模式中再次調用命名捕獲組。 例如,要查找句子中連續的重複單詞,可使用 /\b(?<dup>\w+)\s+\k<dup>\b/:

要將命名捕獲組用於 replace() 方法的替換字符串,你可使用 $<name> 構造。 例如:

後行斷言

ES2018 爲 JavaScript 帶來了後行斷言(lookbehind assertion),該斷言已在其餘語言的正則表達式使用多年。 之前,JavaScript 只支持先行斷言(lookahead assertion)。 後行斷言用 (?<=...) 表示,使你可以根據模式以前的子字符串匹配模式。 例如,若是你想要在不捕獲貨幣符號的狀況下以美圓,英鎊或歐元匹配產品的價格,你可使用/(?<=\$|£|€)\d+(\.\d*)?/:

還有一個負向的後行斷言,用 (?<!...) 表示。 負向後行斷言容許你匹配不跟在某後行斷言以後的模式(譯者注:差點把我本身都繞暈了😷 舉個簡單的例子(?<!a)b: 斷言 b 前面沒有 a,匹配 bb 但不匹配 ab,最終捕獲 b)。 例如,模式 /(?<!un)available/ 可在無 「un」 前綴的狀況下匹配 available:

Unicode 屬性轉義

ES2018 提供了一種稱爲 Unicode 屬性轉義的新轉義序列類型,它在正則表達式中提供對完整 Unicode 的支持。 假設你要匹配字符串中的 Unicode 字符 ㉛. 雖然咱們認爲 ㉛ 是一個數字,可是咱們不能用 \d 匹配它,由於它只支持 ASCII [0-9] 字符。此外,Unicode 屬性轉義也可用於匹配 Unicode 中的任何十進制數:

一樣,若是要匹配任意 Unicode 單詞(劃掉)字母字符,可使用 \p{Alphabetic}:

還有一個否認版本的\p{...},用\P{...},表示:

除了字母和數字以外,還有幾個屬性能夠在 Unicode 屬性轉義中使用。 你能夠在當前規範提案中找到支持的 Unicode 屬性列表。

地址:https://tc39.github.io/proposal-regexp-unicode-property-escapes/#sec-static-semantics-unicodematchproperty-p

RegExp 新特性支持

Node.js:

  • 8.3.0(須要 --harmony 運行時 flag)

  • 8.10.0(支持 s(dotAll) 標誌和後行斷言)

  • 10.0.0(徹底支持)


模板字符串修訂

當模板字符串緊跟在表達式以後時,它會被稱爲標記模板字符串。 當你想要使用函數解析模板字符串時,標記模板會派上用場。 看看這個例子:

上面的代碼調用了標記表達式(它是常規函數)並傳遞模板字符串。 該函數只是修改字符串的動態部分並返回它。

在 ES2018 以前,標記的模板字符串具備與轉義序列相關的語法限制。 反斜槓後跟某些字符序列被視爲特殊字符:\x 被解析爲十六進制轉義符,\u 被解析爲unicode轉義符,\_ 後跟一個數字被解析爲八進制轉義符。 所以,解釋器將諸如 "C:\xxx\uuu""\ubuntu" 之類的字符串視爲無效的轉義序列,並將拋出SyntaxError.

ES2018 從標記模板中刪除了這些限制,它會將無效轉義序列表示爲 undefined,而不是拋出錯誤:

請記住,在常規模板字符串中使用非法轉義序列仍會報錯:

模板字符串修訂支持

Node.js:

  • 8.3.0 (須要 --harmony 運行時 flag)

  • 8.10.0(徹底支持)


總結

咱們已經仔細研究了 ES2018 中引入的幾個關鍵特性,包括異步迭代,rest/spread 屬性,Promise.prototype.finally以及 RegExp對象的新增特性。 雖然有些瀏覽器廠商還沒有徹底實現其中一些功能,但因爲有 Babel 這樣的 JavaScript 轉換器,咱們仍能夠在今天使用它們。

ECMAScript 正在迅速發展,而且每隔一段時間就會引入新功能,歡迎查看完整提案列表👏,瞭解所有新功能。 有啥功能讓你特別興奮嗎?快快和我分享叭~

提案地址:https://github.com/tc39/proposals/blob/master/finished-proposals.md


原文地址:https://css-tricks.com/new-es2018-features-every-javascript-developer-should-know/


好文推薦:

一塊兒來燃燒 Bundle 的「卡路里」



「UC國際技術」致力於與你共享高質量的技術文章

歡迎關注咱們的公衆號、將文章分享給你的好友

相關文章
相關標籤/搜索