[譯] ES2018(ES9)的新特性

Mello_Z發表於2018-06-20

原文連結:www.sitepoint.com/es2018-what…

在這篇文章中,我將介紹ES2018(ES9)的新特性,並介紹如何使用它們。

JavaScript(ECMAScript)是跨多個平臺的許多廠商實施的不斷髮展的標準。ES6(ECMAScript 2015)花費六年的時間敲定,是一個很大的發行版。新的年度釋出流程被制定,以簡化流程並更快地新增功能。 ES9(ES2018)是撰寫本文時的最新版本。

TC39由包括瀏覽器廠商在內的各方組成,他們開會推動JavaScript提案沿著一條嚴格的發展道路前進:

  • Stage 0: strawman——最初想法的提交。
  • Stage 1: proposal(提案)——由TC39至少一名成員倡導的正式提案檔案,該檔案包括API事例。
  • Stage 2: draft(草案)——功能規範的初始版本,該版本包含功能規範的兩個實驗實現。
  • Stage 3: candidate(候選)——提案規範通過審查並從廠商那裡收集反饋
  • Stage 4: finished(完成)——提案准備加入ECMAScript,但是到瀏覽器或者Nodejs中可能需要更長的時間

ES2016

ES2016新增了兩個小的特性來說明標準化過程:

  1. 陣列includes()方法,用來判斷一個陣列是否包含一個指定的值,根據情況,如果包含則返回true,否則返回false
  2. a ** b指數運算子,它與 Math.pow(a, b)相同。

ES2017

ES2017提供了更多的新特性:

  1. Async 函式呈現更清晰的 Promise 語法
  2. Object.values 方法返回一個給定物件自己的所有可列舉屬性值的陣列,值的順序與使用for...in迴圈的順序相同(區別在於for...in迴圈列舉原型鏈中的屬性)
  3. Object.entries()方法返回一個給定物件自身可列舉屬性的鍵值對陣列,其排列與使用for...in迴圈遍歷改物件時返回的順序一致(區別在於for...in迴圈也列舉原型鏈中的屬性)
  4. Object.getOwnPropertyDescriptors()返回一個物件的所有自身屬性的描述符(.value,.writable,.get,.set,.configurable,enumerable
  5. padStart()padEnd(),填充字串達到當前長度
  6. 結尾逗號,陣列定義和函式引數列表
  7. ShareArrayBufferAtomics用於從共享記憶體位置讀取和寫入

關於ES2017的更多資訊請參閱 What's New in ES2017

ES2018

ECMAScript 2018(或者叫ES9)現在已經可用了。以下功能已經到達 stage 4,但是在撰寫本文時在各個瀏覽器的實現還不完整。

非同步迭代

async/await的某些時刻,你可能嘗試在同步迴圈中呼叫非同步函式。例如:

async function process(array) {
  for (let i of array) {
    await doSomething(i);
  }
}
複製程式碼

這段程式碼不會正常執行,下面這段同樣也不會:

async function process(array) {
  array.forEach(async i => {
    await doSomething(i);
  });
}
複製程式碼

這段程式碼中,迴圈本身依舊保持同步,並在在內部非同步函式之前全部呼叫完成。

ES2018引入非同步迭代器(asynchronous iterators),這就像常規迭代器,除了next()方法返回一個Promise。因此await可以和for...of迴圈一起使用,以序列的方式執行非同步操作。例如:

async function process(array) {
  for await (let i of array) {
    doSomething(i);
  }
}
複製程式碼

Promise.finally()

一個Promise呼叫鏈要麼成功到達最後一個.then(),要麼失敗觸發.catch()。在某些情況下,你想要在無論Promise執行成功還是失敗,執行相同的程式碼,例如清除,刪除對話,關閉資料庫連線等。

.finally()允許你指定最終的邏輯:

function doSomething() {
  doSomething1()
  .then(doSomething2)
  .then(doSomething3)
  .catch(err => {
    console.log(err);
  })
  .finally(() => {
    // finish here!
  });
}
複製程式碼

Rest/Spread 屬性

ES2015引入了Rest引數擴充套件運算子。三個點(...)僅用於陣列。Rest引數語法允許我們將一個布丁數量的參數列示為一個陣列。

restParam(1, 2, 3, 4, 5);

function restParam(p1, p2, ...p3) {
  // p1 = 1
  // p2 = 2
  // p3 = [3, 4, 5]
}
複製程式碼

展開操作符以相反的方式工作,將陣列轉換成可傳遞給函式的單獨引數。例如Math.max()返回給定數字中的最大值:

const values = [99, 100, -1, 48, 16];
console.log( Math.max(...values) ); // 100
複製程式碼

ES2018為物件解構提供了和陣列一樣的Rest引數()和展開操作符,一個簡單的例子:

const myObject = {
  a: 1,
  b: 2,
  c: 3
};

const { a, ...x } = myObject;
// a = 1
// x = { b: 2, c: 3 }
複製程式碼

或者你可以使用它給函式傳遞引數:

restParam({
  a: 1,
  b: 2,
  c: 3
});

function restParam({ a, ...x }) {
  // a = 1
  // x = { b: 2, c: 3 }
}
複製程式碼

跟陣列一樣,Rest引數只能在宣告的結尾處使用。此外,它只適用於每個物件的頂層,如果物件中巢狀物件則無法適用。

擴充套件運算子可以在其他物件內使用,例如:

const obj1 = { a: 1, b: 2, c: 3 };
const obj2 = { ...obj1, z: 26 };
// obj2 is { a: 1, b: 2, c: 3, z: 26 }
複製程式碼

可以使用擴充套件運算子拷貝一個物件,像是這樣obj2 = {...obj1},但是 這只是一個物件的淺拷貝。另外,如果一個物件A的屬性是物件B,那麼在克隆後的物件cloneB中,該屬性指向物件B。

正規表示式命名捕獲組(Regular Expression Named Capture Groups)

JavaScript正規表示式可以返回一個匹配的物件——一個包含匹配字串的類陣列,例如:以YYYY-MM-DD的格式解析日期:

const
  reDate = /([0-9]{4})-([0-9]{2})-([0-9]{2})/,
  match  = reDate.exec('2018-04-30'),
  year   = match[1], // 2018
  month  = match[2], // 04
  day    = match[3]; // 30
複製程式碼

這樣的程式碼很難讀懂,並且改變正規表示式的結構有可能改變匹配物件的索引。

ES2018允許命名捕獲組使用符號?<name>,在開啟捕獲括號(後立即命名,示例如下:

const
  reDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/,
  match  = reDate.exec('2018-04-30'),
  year   = match.groups.year,  // 2018
  month  = match.groups.month, // 04
  day    = match.groups.day;   // 30
複製程式碼

任何匹配失敗的命名組都將返回undefined

命名捕獲也可以使用在replace()方法中。例如將日期轉換為美國的 MM-DD-YYYY 格式:

const
  reDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/,
  d      = '2018-04-30',
  usDate = d.replace(reDate, '$<month>-$<day>-$<year>');
複製程式碼

正規表示式反向斷言(lookbehind)

目前JavaScript在正規表示式中支援先行斷言(lookahead)。這意味著匹配會發生,但不會有任何捕獲,並且斷言沒有包含在整個匹配欄位中。例如從價格中捕獲貨幣符號:

const
  reLookahead = /\D(?=\d+)/,
  match       = reLookahead.exec('$123.89');

console.log( match[0] ); // $
複製程式碼

ES2018引入以相同方式工作但是匹配前面的反向斷言(lookbehind),這樣我就可以忽略貨幣符號,單純的捕獲價格的數字:

const
  reLookbehind = /(?<=\D)\d+/,
  match        = reLookbehind.exec('$123.89');

console.log( match[0] ); // 123.89
複製程式碼

以上是 肯定反向斷言,非數字\D必須存在。同樣的,還存在 否定反向斷言,表示一個值必須不存在,例如:

const
  reLookbehindNeg = /(?<!\D)\d+/,
  match           = reLookbehind.exec('$123.89');

console.log( match[0] ); // null
複製程式碼

正規表示式dotAll模式

正規表示式中點.匹配除回車外的任何單字元,標記s改變這種行為,允許行終止符的出現,例如:

/hello.world/.test('hello\nworld');  // false
/hello.world/s.test('hello\nworld'); // true
複製程式碼

正規表示式 Unicode 轉義

到目前為止,在正規表示式中本地訪問 Unicode 字元屬性是不被允許的。ES2018新增了 Unicode 屬性轉義——形式為\p{...}\P{...},在正規表示式中使用標記 u (unicode) 設定,在\p塊兒內,可以以鍵值對的方式設定需要匹配的屬性而非具體內容。例如:

const reGreekSymbol = /\p{Script=Greek}/u;
reGreekSymbol.test('π'); // true
複製程式碼

此特性可以避免使用特定 Unicode 區間來進行內容型別判斷,提升可讀性和可維護性。

非轉義序列的模板字串

最後,ES2018 移除對 ECMAScript 在帶標籤的模版字串中轉義序列的語法限制。

之前,\u開始一個 unicode 轉義,\x開始一個十六進位制轉義,\後跟一個數字開始一個八進位制轉義。這使得建立特定的字串變得不可能,例如Windows檔案路徑 C:\uuu\xxx\111。更多細節參考模板字串

這是所有的ES2018的新特性,ES2019已經開始,你有什麼期待的功能想在明年看到嗎?

譯者參考

相關文章