啊,生活在這樣一個時代真好!
像往常一樣,我為自己安排了太多事情——太多事情看起來都很有趣😄 現在我覺得我的生活中同時有十條故事線在執行,但我的雙手卻不夠用。
但說實話——我不會停止每週的更新。
話雖如此……這次我可不敢再進行深度潛水了。咱們輕鬆點兒吧 😉
前段時間,我的一篇貼文意外走紅:
顯然,這類精心整理的清單正是社區所需要的。
當然,我們有文件。當然,我們可以用谷歌搜尋一切——或者問問克勞德、GPT 或 Gemini。但這裡有個小問題:在搜尋某樣東西之前,你首先需要知道它存在。
所以這次我決定介紹一些 ECMAScript 標準的新新增功能——這些功能是近年來推出的,並且在現代環境中已經可用。
這個話題對我來說並不陌生。早在 2019 年,我就在 meet.js Summit 上做過一個題為「超越 ES6——炒作之後會是什麼?」(大概是這個意思😄)的演講。如果你想回顧一下 2015 年至 2019 年的 ECMAScript 特性,或許還能在 YouTube 上找到當時的錄影。
好了,介紹就到此為止。
以下列出的是我近年來最喜歡的一些現代 JavaScript 特性。我並沒有列出所有特性,只列出了那些實用、有趣或強大的特性。
你會發現……它們都相互關聯,構成了一個更大的模式。
await它解決了什麼問題:
一項不錯的體驗優化。在此之前,你不能直接在模組頂層使用await 。你必須把所有程式碼都包裹在一個 async 函數裡,只是為了載入組態或初始化資料。這雖然不是什麼大問題,但說實話有點多餘。
舊方法(額外樣板程式碼):
async function init() {
const config = await fetchConfig();
startApp(config);
}
init();
現在:
const config = await fetchConfig();
startApp(config);
重要性:
更清晰的啟動邏輯,更少的繁瑣步驟,更容易閱讀。
# )它解決了什麼問題:
說實話,JavaScript 其實從來沒有私有類別欄位。我們只是假裝它有,創造了一些奇怪的約定,例如_privateVar ,它根本不是私有的 😉(當然…除非你用的是 TypeScript)。
現在:
class User {
#id;
constructor(id) {
this.#id = id;
}
}
嘗試在類別外部存取user.#id會引發錯誤。
重要性:
真正的封裝。更安全的抽象,更少的意外修改。
Error.cause它解決了什麼問題:
你曾有多少次因為一個錯誤引發另一個錯誤而浪費半天時間進行除錯,但這兩個錯誤之間的聯繫卻幾乎無法追踪?
舊方法:覆蓋錯誤或手動附加元資料。
現在:
throw new Error("Failed to load data", {
cause: originalError
});
重要性:
更完善的除錯和日誌記錄功能。您可以追蹤完整的故障鏈,而無需猜測。
Object.hasOwn()它解決了什麼問題:
以前,要檢查一個物件是否真的具有某個屬性,需要建立這個令人困惑的怪物:
舊方法:
Object.prototype.hasOwnProperty.call(obj, "key");
現在:
Object.hasOwn(obj, "key");
重要性:
語法更簡潔,更易讀,更少出現邊界情況意外。
.at() — 相對索引它解決了什麼問題:
經典的初級面試題:如何取得陣列的最後一個元素?所有高於初級程度的程式設計師最終都學會了同樣的蹩腳方法。
舊方法:
arr[arr.length - 1];
現在:
arr.at(-1);
重要性:
或許不算革命性的,但肯定更清晰易懂。
本次發布的核心理念只有一個:避免意外突變。
toSorted()問題:
Array.sort()固然好用…但它會修改原始陣列。有人會忘記這一點——結果你的應用程式就一半都出問題了。而有些人記得,所以他們每次都手動複製陣列。
舊方法:
[...arr].sort();
現在:
const sorted = arr.toSorted();
它改變了什麼:
無需修改原始資料即可獲得排序後的副本。
重要性:
對狀態管理和函數式程式設計風格影響巨大。
toReversed()和toSpliced()同樣的理念:複製而不是改變。
arr.toReversed();
arr.toSpliced(2, 1);
重要性:
可預測性。你不會意外地破壞共享同一個陣列引用的程式碼。
findLast() / findLastIndex()問題:
我們之前有find ,但如果想要找到最後一個匹配項呢?以前的解決方法…並不優雅,而且肯定會讓新手感到困惑。
舊方法:
[...arr].reverse().find(fn);
現在:
arr.findLast(fn);
重要性:
更少的噪音,更清晰的意圖——程式碼準確地表達了你的意思。
Object.groupBy()問題:
將陣列分組通常意味著編寫的 reducer 看起來比問題本身還要複雜。
舊方法:
users.reduce((acc, user) => {
(acc[user.role] ??= []).push(user);
return acc;
}, {});
現在:
const grouped = Object.groupBy(users, u => u.role);
重要性:
大幅提升了程式碼可讀性。以前需要輔助函數才能實現的功能,現在只需一行程式碼即可完成。
Promise.withResolvers()問題:
建立外部resolve / reject處理程序一直都很麻煩。
舊方法:
let resolve;
const promise = new Promise(r => resolve = r);
現在:
const { promise, resolve, reject } =
Promise.withResolvers();
重要性:
更清晰的非同步編排-尤其適用於佇列、事件或複雜流程。
問題:
緩衝區過去的大小是固定的,這在處理串流或動態資料時令人沮喪——尤其是如果你是那些喜歡嘗試邊緣 JavaScript、工作進程或二進位資料的怪人之一(例如我😄)。
const buffer = new ArrayBuffer(8, {
maxByteLength: 16
});
重要性:
更靈活的記憶體處理方式,適用於進階應用場景。
問題:
陣列方法固然好用,但它們每一步都會建立中間陣列。有時這是不必要的。
舊方法(會建立額外的陣列):
const result = arr
.map(x => x * 2)
.filter(x => x > 5)
.slice(0, 3);
每一步都會分配一個新的陣列。
現在(延遲處理):
const result = iterator
.map(x => x * 2)
.filter(x => x > 5)
.take(3)
.toArray();
它取代了:
手動生成器管道或性能密集型陣列鏈。
重要性:
值是逐步處理的(惰性求值)
減少分配
在大資料集上表現更佳
更函數式風格的管道
想想:要有串流的思維模式,而不是「建立另一個陣列」。
問題:
更高級的集合邏輯總是需要自訂輔助函數或笨拙的陣列轉換。
舊方法:
const intersection = new Set(
[...a].filter(x => b.has(x))
);
現在:
a.intersection(b);
a.union(b);
a.difference(b);
重要性:
直接在語言中實現類似數學運算。減少樣板程式碼,更清晰地表達意圖。
RegExp.escape()問題:
安全性。根據使用者輸入建立正規表示式很容易破壞模式,甚至引入安全漏洞。
舊方法:
const safe = userInput.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const regex = new RegExp(safe);
現在:
const regex = new RegExp(RegExp.escape(userInput));
重要性:
無需每次都編寫自己的輔助函數,即可更安全地建立正規表示式。
Promise.try()問題:
有時你會想以相同的方式處理同步程式碼和非同步程式碼——尤其是在同步函數可能會拋出異常的情況下。
現在:
await Promise.try(() => mightThrow());
重要性:
一切都將自動基於 Promise,從而簡化錯誤處理流程。
JavaScript 在處理數字方面一直有點笨拙——預設情況下是 64 位元浮點數。我們早就有了Float32Array ,它已經很有用了,但現在 JS 更進一步。
const data = new Float16Array(1024);
這實際上意味著:
較小的數值表示(16 位元)
降低記憶體佔用
在某些 GPU/ML 場景中,資料傳輸速度更快
重要性:
圖形、WebGPU、機器學習和以效能為導向的工作負載都能從更緊湊的資料中受益。
如果縮小畫面,你會注意到一個規律:
較少的突變
更清晰的意圖
更安全的非同步處理
更強大的資料處理功能
JavaScript 的發展不再像以前那樣伴隨著翻天覆地的變革了。
它透過一些小的、實用的升級不斷演進,悄悄讓日常程式碼更簡潔、更易於理解。
說實話——這正是我最喜歡的進化方式🙂
原文出處:https://dev.to/sylwia-lask/16-modern-javascript-features-that-might-blow-your-mind-4h5e