跳至主要内容

Promise 組合子

· 閱讀時間約 4 分鐘
Mathias Bynens ([@mathias](https://twitter.com/mathias))

自從在 ES2015 引入 Promise 以來,JavaScript 就支持了兩種 Promise 組合子:靜態方法 Promise.allPromise.race

目前有兩個新的提案正在標準化過程中:Promise.allSettledPromise.any。隨著這些新增內容,JavaScript 共有四種 Promise 組合子,每一種都支持不同的使用場景。

`Array.prototype.flat` 和 `Array.prototype.flatMap`

· 閱讀時間約 2 分鐘
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Array.prototype.flat

此範例中的陣列是多層嵌套的:它包含一個陣列,而這個陣列又包含另一個陣列。

const array = [1, [2, [3]]];
// ^^^^^^^^^^^^^ 外層陣列
// ^^^^^^^^ 內層陣列
// ^^^ 最內層陣列

Array#flat 回傳一個展平後的陣列。

array.flat();
// → [1, 2, [3]]

// …等同於:
array.flat(1);
// → [1, 2, [3]]

預設的展平深度是 1,但您可以傳入任何數字值來遞迴展平到該深度。若要持續展平直到結果不再包含嵌套陣列,可以使用 Infinity

// 持續遞迴展平直到陣列不再包含嵌套陣列:
array.flat(Infinity);
// → [1, 2, 3]

這個方法為什麼叫做 Array.prototype.flat 而不是 Array.prototype.flatten 呢?閱讀我們的 #SmooshGate 撰寫內容來了解!

Array.prototype.flatMap

以下是另一個範例。我們有一個 duplicate 函數,它接受一個值並回傳一個包含該值兩次的陣列。如果我們將 duplicate 套用到陣列中的每個值,我們會得到一個嵌套陣列。

const duplicate = (x) => [x, x];

[2, 3, 4].map(duplicate);
// → [[2, 2], [3, 3], [4, 4]]

接著您可以對結果呼叫 flat 來展平陣列:

[2, 3, 4].map(duplicate).flat(); // 🐌
// → [2, 2, 3, 3, 4, 4]

由於此模式在函數式編程中相當常見,因此現在有一個專屬的 flatMap 方法。

[2, 3, 4].flatMap(duplicate); // 🚀
// → [2, 2, 3, 3, 4, 4]

flatMap 比起分別執行 mapflat 更加高效。

flatMap 的使用案例感興趣嗎?請查看 Axel Rauschmayer 的解釋

Array#{flat,flatMap} 支援

數字分隔符

· 閱讀時間約 2 分鐘
Mathias Bynens ([@mathias](https://twitter.com/mathias))

大型數字字面值難以讓人眼快速解析,尤其是當數字中有許多重複的數字時:

1000000000000
1019436871.42

為了提升可讀性,一項新的 JavaScript 語言特性允許在數字字面值中使用底線作為分隔符。因此,可以將上述數字重新編寫,以每千位分組為例:

`String.prototype.matchAll`

· 閱讀時間約 3 分鐘
Mathias Bynens ([@mathias](https://twitter.com/mathias))

在字串中重複套用同一個正則表達式以獲取所有匹配的情況並不罕見。某種程度上,這已經可以通過使用 String#match 方法實現。

在這個例子中,我們找到所有僅由十六進位數字組成的字,然後記錄每個匹配項:

const string = 'Magic hex numbers: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
for (const match of string.match(regex)) {
console.log(match);
}

// 輸出:
//
// 'DEADBEEF'
// 'CAFE'

然而,這只會給你匹配的 子字串。通常,你不僅想要子字串,還希望獲取附加資訊,如每個子字串的索引,或者每次匹配時的捕捉群組。

這可以通過撰寫自己的迴圈並手動跟蹤匹配物件來實現,但這有點麻煩且不太方便:

const string = 'Magic hex numbers: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
let match;
while (match = regex.exec(string)) {
console.log(match);
}

// 輸出:
//
// [ 'DEADBEEF', index: 19, input: 'Magic hex numbers: DEADBEEF CAFE' ]
// [ 'CAFE', index: 28, input: 'Magic hex numbers: DEADBEEF CAFE' ]

新的 String#matchAll API 使這變得前所未有的簡單:現在你可以撰寫一個簡單的 for-of 迴圈以獲取所有匹配物件。

const string = 'Magic hex numbers: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
for (const match of string.matchAll(regex)) {
console.log(match);
}

// 輸出:
//
// [ 'DEADBEEF', index: 19, input: 'Magic hex numbers: DEADBEEF CAFE' ]
// [ 'CAFE', index: 28, input: 'Magic hex numbers: DEADBEEF CAFE' ]

String#matchAll 對於具有捕捉群組的正則表達式特別有用。它會提供每個匹配的完整資訊,包括捕捉群組。

const string = 'Favorite GitHub repos: tc39/ecma262 v8/v8.dev';
const regex = /\b(?<owner>[a-z0-9]+)\/(?<repo>[a-z0-9\.]+)\b/g;
for (const match of string.matchAll(regex)) {
console.log(`${match[0]} at ${match.index} with '${match.input}'`);
console.log(`→ owner: ${match.groups.owner}`);
console.log(`→ repo: ${match.groups.repo}`);
}

`Intl.ListFormat`

· 閱讀時間約 3 分鐘
Mathias Bynens ([@mathias](https://twitter.com/mathias)) 和 Frank Yung-Fong Tang

現代的網絡應用通常使用包含動態數據的列表。例如,一個相片查看應用可能顯示如下內容:

此相片包括 Ada、Edith、 Grace

一款文字遊戲可能會顯示另一種類型的列表:

選擇你的超能力:隱形、心靈控制、 共情能力

由於每種語言的列表格式化習慣和詞語各不相同,實現一個本地化的列表格式化器並非易事。不僅需要獲取所有希望支持語言中的相關詞語(如上例中的 “and” 或 “or”),還需要對所有這些語言的格式化習慣進行編碼!Unicode CLDR 提供這些數據,但要在 JavaScript 中使用它,這些數據需嵌入並隨其他庫代碼一起傳遞。這樣不幸會增加庫的捆綁大小,進而對加載時間、解析/編譯成本及內存消耗造成負面影響。

模組命名空間導出

· 閱讀時間約 1 分鐘
Mathias Bynens ([@mathias](https://twitter.com/mathias))

JavaScript 模組 中,先前已經可以使用以下語法:

import * as utils from './utils.mjs';

然而,並沒有對稱的 export 語法……直到現在

export * as utils from './utils.mjs';

這相當於以下:

import * as utils from './utils.mjs';
export { utils };

公開與私有類別字段

· 閱讀時間約 4 分鐘
Mathias Bynens ([@mathias](https://twitter.com/mathias))

多個提案擴展了現有的 JavaScript 類別語法,提供了新功能。本文章解釋了 V8 v7.2 和 Chrome 72 中新增的公開類別字段語法,以及即將推出的私有類別字段語法。

以下是一個創建名為 IncreasingCounter 的類別實例的程式碼範例:

const counter = new IncreasingCounter();
counter.value;
// 記錄 '獲取目前的值!'
// → 0
counter.increment();
counter.value;
// 記錄 '獲取目前的值!'
// → 1

請注意,存取 value 會先執行某些程式碼(例如,記錄訊息)然後才返回結果。現在問問自己,您會如何在 JavaScript 中實作這個類別?🤔

ES2015 類別語法

以下是使用 ES2015 類別語法實作 IncreasingCounter 的方式:

class IncreasingCounter {
constructor() {
this._count = 0;
}
get value() {
console.log('獲取目前的值!');
return this._count;
}
increment() {
this._count++;
}
}

此類別在原型上安裝了 value 取得器和 increment 方法。更有趣的是,這個類別有一個建構子會創建 _count 實例屬性並將其預設值設定為 0。我們目前傾向使用底線前綴來表示 _count 不應直接被類別的用戶使用,但這只是一種慣例;它並不是語言強制的 真正 “私有”屬性。

`Intl.RelativeTimeFormat`

· 閱讀時間約 5 分鐘
Mathias Bynens ([@mathias](https://twitter.com/mathias))

現代的網頁應用程式通常會使用「昨天」、「42秒前」或「3個月後」此類短語,而不是完整的日期和時間戳。這些 相對時間格式化值 已變得如此常見,以至於許多流行的函式庫都實作了能夠以本地化方式格式化它們的工具函數。(例子包括 Moment.jsGlobalizedate-fns。)

格式良好的 `JSON.stringify`

· 閱讀時間約 1 分鐘
Mathias Bynens ([@mathias](https://twitter.com/mathias))

JSON.stringify 之前的規範是當輸入包含任何孤立代理項時,返回格式不良的 Unicode 字串:

JSON.stringify('\uD800');
// → '"�"'

“格式良好的 JSON.stringify”提案 修改了 JSON.stringify,使其對孤立代理項輸出轉義序列,令其輸出有效 Unicode(並且可在 UTF-8 中表示):

JavaScript 模組

· 閱讀時間約 20 分鐘
Addy Osmani ([@addyosmani](https://twitter.com/addyosmani)) 和 Mathias Bynens ([@mathias](https://twitter.com/mathias))

JavaScript 模組現在已經被所有主流瀏覽器支援!

本文說明如何使用 JS 模組、如何負責任地部署它們,以及 Chrome 團隊如何努力在未來改進模組。

什麼是 JS 模組?

JS 模組(也稱為“ES 模組”或“ECMAScript 模組”)是一個重要的新功能,或者說是一組新的功能。過去您可能使用過用戶層的 JavaScript 模組系統,也許使用過像 Node.js 中的 CommonJS,或者 AMD,或者其他系統。所有這些模組系統的共同點是:它們允許您導入和導出內容。