原文作者:Faraz Kelhini
譯者:UC 國際研發 Jothy
寫在最前:歡迎你來到“UC國際技術”公眾號,我們將為大家提供與客戶端、服務端、演算法、測試、資料、前端等相關的高質量技術文章,不限於原創與翻譯。
編者按:曾幾何時,年少的我捧著阮一峰老師的《ES6 標準入門》,感嘆 JS 變遷實在太快,好怕學不動了。直至寫了幾年 ES6 的今日,回頭看方知:不要為了學 ES X 而學 ES X,無論 ES 幾其實都是語法糖,是輔助角色,重點是想清楚它能為我們的開發帶來什麼好處,而不是本末倒置。今天介紹的 ES2018 新特性還是有蠻多亮點的,一起來看看吧。
ECMAScript 標準的第九版,官宣為 ECMAScript 2018(或簡稱 ES2018),已於 2018 年 6 月釋出。從 ES2016 開始,每隔一年就會發布一版 ECMAScript 規範的新版本,並新增不多於主版本的功能。 最新的這個版本延續了每年釋出的週期,新增了四個新的 RegExp
特性,rest/spread 屬性,非同步迭代和 Promise.prototype.finally
。 此外,它還從標記模板中刪除了轉義序列的語法限制。
我們將在後面的小節中詳細解釋這些新變化 ?。
rest/spread 屬性
回顧 ES2015,最有趣的功能當數 spread 運算子。 該運算子極大簡化了陣列的複製及合併。你可以使用它替換concat()
或slice()
方法:
在必須將陣列的各個項分別作為引數傳入函式的情況下,擴充套件運算子也派得上用場。 例如:
通過向物件文法新增 spread 屬性,ES2018 進一步擴充套件了此語法。 使用 spread 屬性,你可以將物件自身的可列舉屬性複製到新物件上。舉個例子 ?:
在此程式碼中,...
運算子用於檢索 obj1 的屬性並將它們分配給 obj2。 在 ES2018 之前,這麼做會報錯的。 如果出現多個屬性同名的情況,將會取最後一個值:
Spread 屬性還提供了一種合併兩個或多個物件的新方法,可以替代 Object.assign()
方法使用:
在此程式碼中,Object.assign()
方法會執行其繼承的 setter 屬性,而 spread 屬性則完全忽略了這一步。
切記!spread 屬性只複製可列舉屬性。 在下面的例子中,type
屬性不會出現在複製出的物件中,因為其enumerable
屬性為 false
:
繼承屬性即使是可列舉的,也會被忽略:
在這段程式碼中,car2
繼承了car
的color
屬性。 由於 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 屬性支援
8.0.0(需要 --harmony 執行時 flag)
8.3.0(完全支援)
非同步迭代
迭代資料集是程式設計的重要組成部分。 在 ES2015 之前,JavaScript提供了for
,for...in
和 while
等語句,以及map()
,filter()
和 forEach()
等方法。 為了方便程式設計師一個個地處理集合元素,ES2015 引入了迭代器介面。
如果物件具有 Symbol.iterator
屬性,則表示它是可迭代的。 在 ES2015 中,字串和集合物件(如Set
, Map
和 Array
)帶有Symbol.iterator
屬性,因此是可迭代的。 以下程式碼說明了如何每次訪問一個可迭代元素☝️:
Symbol.iterator
是個廣為人知的符號,用於表示返回迭代器的函式。與迭代器互動主要使用next()
方法。此方法返回一個具有value
和done
兩個屬性的物件。 value
屬性包含集合中下一個元素的值。done
屬性包含true
或false
,表明是否已到達集合的末尾。
預設情況下普通物件不可迭代,但如果在其上定義了 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
不能與非同步可迭代物件一起使用,因為 value
和 done
不是同步產生的。 出於這個原因,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/
好文推薦:
“UC國際技術”致力於與你共享高質量的技術文章
歡迎關注我們的公眾號、將文章分享給你的好友