Skip to main content

12 個現代 JavaScript 語言特性

JavaScript 每年都在悄悄長出新語法,但多數人還是用三五年前的寫法在寫。這篇整理 12 個「可讀性、安全性、可維護性」都有感提升的現代特性,並附上「舊寫法 vs 新寫法」對照,方便挑幾個先在專案裡替換掉。

其中 Array.at()?? / || 已有獨立筆記,這裡只做重點與情境,細節請看文末連結。

1. Optional Chaining(?.):安全存取深層屬性

存取巢狀屬性時,只要中途某一層是 null / undefined 就會直接丟錯。?. 讓存取鏈遇到空值時安全回傳 undefined,而不是拋出例外。

// 舊寫法:層層防呆
const city = user && user.address && user.address.city;

// 新寫法
const city = user?.address?.city;

常見情境:API 回應、React props、非同步資料、選填設定物件。也可以搭配函式呼叫 obj.method?.() 與動態索引 obj?.[key]

2. Nullish Coalescing(??):只在 null / undefined 才 fallback

|| 會把 0false'' 這些「falsy 但有效」的值也當成要被取代,容易產生 bug。?? 只在值為 nullundefined 時才使用預設值。

const count1 = 0 || 10; // 10(多半不是你要的)
const count2 = 0 ?? 10; // 0(正確保留 0)

??|| 的完整比較另有筆記,見 你沒有注意到的 js

3. Array.at():支援負索引的元素存取

取最後一個元素不用再寫 arr[arr.length - 1],直接用 .at(-1)

const numbers = [10, 20, 30];
numbers.at(-1); // 30

陣列相關的現代方法(.at().findLast().toSorted() 等)詳見 現代 JavaScript 陣列新方法

4. structuredClone():真正的深拷貝

過去常用 JSON.parse(JSON.stringify(obj)) 做深拷貝,但它會壞掉:DateMapSetundefined 都會遺失,也不支援循環參照。structuredClone() 是瀏覽器與 Node 內建的深拷貝方案。

const user = {
name: "Alex",
createdAt: new Date(),
settings: { darkMode: true },
};

const cloned = structuredClone(user);

在 React state、Redux、複雜物件更新時特別實用。

深/淺拷貝的其他做法可對照 常用的東西

5. Object.hasOwn():更可靠的「自有屬性」判斷

obj.hasOwnProperty(...) 在某些邊界情況會失效(例如物件本身覆寫了 hasOwnProperty,或用 Object.create(null) 建立的物件根本沒有這個方法)。Object.hasOwn() 是更安全的靜態方法。

const user = { name: "Alex" };

Object.hasOwn(user, "name"); // true

6. 邏輯賦值運算子(||=&&=??=

把「邏輯判斷 + 賦值」合成一步,減少重複程式碼。

// 舊寫法
if (!user.name) {
user.name = "Guest";
}

// 新寫法
user.name ||= "Guest"; // 為 falsy 時才賦值
settings.theme ??= "dark"; // 為 null / undefined 時才賦值
config.enabled &&= isReady; // 為 truthy 時才賦值

在設定值很多的場景(config、預設值)特別省事。

7. Promise.allSettled():每個 Promise 都獨立完成

Promise.all() 只要有一個 reject,整組就失敗、拿不到任何結果。Promise.allSettled() 會等全部完成,逐一回傳 fulfilledrejected 狀態。

const results = await Promise.allSettled([
fetchUser(),
fetchPosts(),
fetchComments(),
]);

// results[i] = { status: 'fulfilled', value } 或 { status: 'rejected', reason }

適合 dashboard、多來源 API、需要「部分失敗也要顯示其他資料」的韌性場景。

Promise.all / Promise.race 的差異見 你沒有注意到的 js

8. String.prototype.replaceAll():全部取代不用正則

以前要「全部取代」得寫帶 g 旗標的正則,現在可直接傳字串。

// 舊寫法
text.replace(/react/g, "React Native");

// 新寫法
text.replaceAll("react", "React Native");

9. 數字分隔符(Numeric Separators):大數字更好讀

用底線 _ 分隔數字,純粹是視覺輔助,不影響數值。

const budget = 1_000_000_000; // === 1000000000

在金融、統計、大型數據常數上很有感。

10. Top-Level Await:模組頂層直接 await

在 ES module 中,可以直接於頂層使用 await,不用再包一層 async 立即執行函式。

// 舊寫法
async function init() {
const data = await fetchData();
}
init();

// 新寫法(於 ES module 頂層)
const data = await fetchData();

適合初始化邏輯、載入設定、啟動流程。注意:只能在 ES module(type="module".mjs)使用,且會延後模組的載入完成時間。

11. WeakMapWeakSet:不阻止垃圾回收

Map / Set 的差別在於「弱參照」:當 key 物件沒有其他參照時,會被垃圾回收,不會造成記憶體洩漏。key 只能是物件,且不可疊代。

const cache = new WeakMap();

function getMetadata(obj) {
if (!cache.has(obj)) {
cache.set(obj, computeExpensiveData(obj));
}
return cache.get(obj);
}

適合快取、DOM 節點對應的附加資料、私有 metadata 等對記憶體敏感的場景。

Map / Set 的完整用法見 你沒有注意到的 js

12. Object.groupBy():一行完成分組

以前分組要寫一坨 reduce,現在可用 Object.groupBy() 依回呼函式回傳的 key 分組。

const users = [
{ name: "Alex", role: "admin" },
{ name: "Sarah", role: "user" },
{ name: "Mike", role: "admin" },
];

const grouped = Object.groupBy(users, (user) => user.role);
// { admin: [{...Alex}, {...Mike}], user: [{...Sarah}] }

這是相對新的 API(ES2024),使用前留意目標環境的支援度;需要更廣支援時可改用 Map.groupBy() 或自行以 reduce 實作。


為什麼值得關心這些特性

現代 JavaScript 不只是語法變短,而是實際改善:

面向帶來的好處
可讀性語意清楚,一眼看懂意圖
安全性少踩 falsy 值、深拷貝失真、屬性判斷等坑
可維護性少寫樣板程式碼,減少 bug 表面積
韌性allSettled 等讓部分失敗不拖垮整體

在大型應用裡,這些小改善會隨時間複利累積。可以先從 ?.??.at(-1)structuredClone() 這幾個高頻又安全的開始替換。

參考資料