頂層`await`
頂層await
使開發者能夠在 async 函數之外使用 await
關鍵字。它像是一個大的 async 函數,會讓其他import
它的模組在開始評估它們的主體之前等待。
頂層await
使開發者能夠在 async 函數之外使用 await
關鍵字。它像是一個大的 async 函數,會讓其他import
它的模組在開始評估它們的主體之前等待。
新的 Nullish 合併提案 (??
) 添加了一個短路運算符,用於處理默認值。
你可能已經熟悉其他短路運算符 &&
和 ||
。這兩個運算符處理“真值”和“假值”。假設代碼示例 lhs && rhs
,如果 lhs
(讀作 左側)是假值,該表達式求值為 lhs
。否則,則求值為 rhs
(讀作 右側)。而在代碼示例 lhs || rhs
中則相反。如果 lhs
是真值,該表達式求值為 lhs
。否則,則求值為 rhs
。
在 JavaScript 中,長鏈的屬性訪問可能容易出錯,因為其中任何一步都可能評估為 null
或 undefined
(也稱為“空值”)。在每一步進行屬性存在檢查,很容易變成深度嵌套的 if
語句或一個長長的 if
條件,複製屬性訪問的鏈路:
隨著提案 JSON ⊂ ECMAScript,JSON 成為 ECMAScript 的語法子集。如果你對此感到驚訝,並非只有你一個!
在 ES2018 中,ECMAScript 的字串文本不能包含未轉義的 U+2028 行分隔符和 U+2029 段落分隔符字元,因為即使在該上下文中,它們仍被認為是行終結符:
// 包含原始 U+2028 字元的字串。
const LS = '
';
// → ES2018: 語法錯誤
// 一個由 `eval` 生成的,包含原始 U+2029 字元的字串:
const PS = eval('"\u2029"');
// → ES2018: 語法錯誤
這是有問題的,因為 JSON 字串_可以_包含這些字元。因此,開發者在嵌入有效的 JSON 到 ECMAScript 程式中時,必須實現特殊的後處理邏輯來處理這些字元。沒有這樣的邏輯,程式碼可能會有細微的錯誤,甚至導致安全問題。
你可能已經熟悉 Intl.NumberFormat
API,因為它在現代環境中已被支援了一段時間。
在其最基本的形式中,Intl.NumberFormat
讓您可以建立一個可重用的格式器實例,支援語系感知的數字格式化。就像其他 Intl.*Format
API 一樣,格式器實例同時支持 format
與 formatToParts
方法:
如果你曾經為瀏覽器編寫 JavaScript,可能使用過 window
來訪問全域 this
。在 Node.js 中,你可能使用過 global
。如果你編寫了需要同時在這兩個環境中運作的代碼,可能會檢測哪個可用,然後使用它 —— 但隨著你要支持的環境和使用情境增加,需要檢查的標識符列表也會越來越長,事情很快就會變得難以控制:
通常,在 JavaScript 中對物件的引用是 強引用,這意味著只要你擁有對該物件的引用,它就不會被垃圾回收機制回收。
const ref = { x: 42, y: 51 };
// 只要你能訪問 `ref`(或者其他任何對同一物件的引用),該物件就不會被垃圾回收機制回收。
目前,WeakMap
和 WeakSet
是 JavaScript 中唯一可以弱引用物件的方式:將物件作為 WeakMap
或 WeakSet
的鍵添加,並不會防止該物件被垃圾回收機制回收。
const wm = new WeakMap();
{
const ref = {};
const metaData = 'foo';
wm.set(ref, metaData);
wm.get(ref);
// → metaData
}
// 在這個區塊範圍內,我們不再有對 `ref` 的引用,因此它
// 現在可以被垃圾回收機制回收,儘管它是 `wm` 的一個鍵
// 且我們仍然可以訪問 `wm`。
假設你有一個狗狗的陣列,每隻狗有一個名字和一個評級。(如果這聽起來是個奇怪的例子,你應該知道,Twitter 上有一個專門做這件事的賬號……請不要深究!)
// 注意,陣列已按 `name` 字母序排序。
const doggos = [
{ name: 'Abby', rating: 12 },
{ name: 'Bandit', rating: 13 },
{ name: 'Choco', rating: 14 },
{ name: 'Daisy', rating: 12 },
{ name: 'Elmo', rating: 12 },
{ name: 'Falco', rating: 13 },
{ name: 'Ghost', rating: 14 },
];
// 按 `rating` 降序排序狗狗。
// (會就地更新 `doggos`。)
doggos.sort((a, b) => b.rating - a.rating);
JavaScript Symbol
在建立時可以給予一個描述:
const symbol = Symbol('foo');
// ^^^^^
以前,要以程式方式存取這個描述的唯一方法是透過 Symbol.prototype.toString()
間接取得:
const symbol = Symbol('foo');
// ^^^^^
symbol.toString();
// → 'Symbol(foo)'
// ^^^
symbol.toString().slice(7, -1); // 🤔
// → 'foo'
然而,這段程式碼看起來有點神秘,不太容易理解,並且違反了“表達意圖,而不是實作”的原則。上述技術也無法區分沒有描述的 Symbol (即 Symbol()
)和描述為空字串的 Symbol(即 Symbol('')
)。
Object.fromEntries
是 JavaScript 內建函式庫的一個實用新增功能。在解釋它的功能之前,了解現有的 Object.entries
API 會有所幫助。
Object.entries
Object.entries
API 已經存在一段時間了。
對於物件中的每個鍵值對,Object.entries
會返回一個陣列,第一個元素是鍵,第二個元素是值。
Object.entries
尤其在與 for
-of
結合使用時非常有用,因為它能讓你非常優雅地遍歷物件中的所有鍵值對:
const object = { x: 42, y: 50 };
const entries = Object.entries(object);
// → [['x', 42], ['y', 50]]
for (const [key, value] of entries) {
console.log(`The value of ${key} is ${value}.`);
}
// 日誌:
// x 的值是 42。
// y 的值是 50。
不幸的是,直到現在,還沒有一個簡單的方法可以將 entries 結果轉回到等效的物件。
Object.fromEntries
新的 Object.fromEntries
API 執行了 Object.entries
的相反操作。這使得根據其 entries 重建物件變得簡單:
const object = { x: 42, y: 50 };
const entries = Object.entries(object);
// → [['x', 42], ['y', 50]]
const result = Object.fromEntries(entries);
// → { x: 42, y: 50 }
一個常見的用例是轉換物件。現在你可以通過遍歷它的 entries,然後使用你可能已經熟悉的陣列方法來完成:
const object = { x: 42, y: 50, abc: 9001 };
const result = Object.fromEntries(
Object.entries(object)
.filter(([ key, value ]) => key.length === 1)
.map(([ key, value ]) => [ key, value * 2 ])
);
// → { x: 84, y: 100 }
在這個例子中,我們使用 filter
過濾物件來僅保留鍵長度為 1
的鍵,也就是僅保留鍵 x
和 y
,不包括鍵 abc
。接著,我們用 map
遍歷剩下的 entries,並為每個返回更新的鍵值對。此例中,我們通過將值乘以 2
來使每個值加倍。最終結果是一個新物件,僅包含屬性 x
和 y
及其新值。