JavaScript ES6+ 新特性(Modern JS)
主題:Nullish Coalescing(??)
簡介
在日常開發中,我們常常需要為變數提供「備援值」── 也就是當變數為 null、undefined 時,使用一個安全的預設值。過去 JavaScript 主要靠 邏輯或運算子(||)來達成這件事,但 || 會把所有「假值」 (false, 0, '', NaN) 都視為「不存在」,這在許多情境下會導致意外的行為。
ECMAScript 2020 為了解決這個問題,正式加入了 Nullish Coalescing Operator(空值合併運算子)??。它只在左側運算元為 null 或 undefined 時才會返回右側的備援值,讓我們可以更精確地控制「何時使用預設值」。本文將深入介紹 ?? 的運作原理、實務範例、常見陷阱與最佳實踐,幫助你在程式碼中安全、簡潔地處理空值。
核心概念
1. 為什麼需要 ???
| 運算子 | 僅在左側為 null / undefined 時返回右側 |
會把所有「假值」都視為空值 |
|---|---|---|
?? |
✅ | ❌ |
| ` | ` |
const a = 0;
const b = a || 10; // b = 10 (0 被視為 falsy)
const c = a ?? 10; // c = 0 (0 不是 null/undefined)
在需要保留 0、空字串 ''、false 等合法值時,?? 是唯一正確的選擇。
2. 基本語法
let result = value ?? fallback;
value:可能是任意型別的表達式。fallback:當value為null或undefined時使用的備援值。
注意:
??只能與&&、||結合使用,且必須加上括號以避免語法錯誤(a ?? b && c→a ?? (b && c))。
3. 與解構賦值結合
function createUser({ name, age, role } = {}) {
// 若外部傳入的物件缺少屬性,使用預設值
const user = {
name: name ?? '匿名',
age: age ?? 18,
role: role ?? 'guest',
};
return user;
}
解構賦值本身會在缺少屬性時給 undefined,配合 ?? 能一次解決「缺屬性」與「null」兩種情況。
4. 與函式參數的預設值比較
// 使用傳統預設值(只在參數為 undefined 時生效)
function greet(message = 'Hello') {
console.log(message);
}
greet(undefined); // Hello
greet(null); // null (預設值不會生效)
// 使用 ?? 取得相同效果,且同時處理 null
function greet2(message) {
const msg = message ?? 'Hello';
console.log(msg);
}
greet2(undefined); // Hello
greet2(null); // Hello
?? 讓我們在函式內部自行決定何時使用備援值,彈性更高。
5. 多層 Nullish 合併
const config = {
timeout: null,
retry: undefined,
endpoint: '',
};
const finalConfig = {
timeout: config.timeout ?? 3000, // 3000
retry: config.retry ?? 5, // 5
endpoint: config.endpoint ?? '/api', // '' (空字串保留)
};
只要左側不是 null/undefined,即使是空字串也會直接使用,避免不必要的「預設覆寫」。
程式碼範例(實用範例 5 個)
範例 1:表單輸入的預設值
function getFormData(form) {
return {
username: form.username?.value ?? 'guest',
age: Number(form.age?.value) ?? 0,
newsletter: form.newsletter?.checked ?? false,
};
}
?.(optional chaining)先保證不會因form.username為null而拋錯。??確保即使使用者留下空白,仍會得到合理的預設值。
範例 2:API 回傳資料的容錯處理
async function fetchUser(id) {
const res = await fetch(`/api/users/${id}`);
const data = await res.json();
// API 可能回傳 null 或 undefined
const name = data.name ?? '未知使用者';
const email = data.email ?? '未提供';
const avatar = data.avatarUrl ?? '/images/default-avatar.png';
return { id, name, email, avatar };
}
使用 ?? 能在後端資料缺漏時自動補上前端需要的佔位資訊,提升使用者體驗。
範例 3:設定檔的層層覆寫
const defaultOptions = {
theme: 'light',
lang: 'zh-TW',
debug: false,
};
function mergeOptions(userOptions) {
return {
theme: userOptions.theme ?? defaultOptions.theme,
lang: userOptions.lang ?? defaultOptions.lang,
debug: userOptions.debug ?? defaultOptions.debug,
};
}
?? 僅在使用者真的沒有提供值時才採用預設,避免把 false、0 等有效設定意外地改成預設。
範例 4:計算式中的安全除法
function safeDivide(a, b) {
// 若除數為 null/undefined,視為 1;其他 falsy 值 (0) 仍保留
const divisor = b ?? 1;
return a / divisor;
}
console.log(safeDivide(10, 0)); // 0 (除以 0 仍會得到 Infinity,但不是預設值)
console.log(safeDivide(10, null)); // 10
此例展示 ?? 與傳統 || 的差異:b || 1 會把 0 當成 falsy 而改成 1,導致錯誤的計算結果。
範例 5:與 && 結合的條件渲染(React 範例)
function UserBadge({ user }) {
return (
<div className="badge">
{/* 若 user 為 null/undefined,直接顯示「訪客」;若有值則顯示 name */}
{user?.name ?? '訪客'}
</div>
);
}
在 JSX 中 ?? 常與 ?. 搭配,讓 UI 渲染邏輯既簡潔又安全。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方案 |
|---|---|---|
把 0、空字串 ''、false 當作空值 |
使用 ` | |
**與 &&、` |
` 混用忘記加括號** | |
| 在左側產生副作用 | func() ?? fallback 會執行 func(),即使結果不是 null,也會產生副作用。 |
確保左側表達式是「純」的或自行控制副作用。 |
錯把 null 視為合法值 |
某些 API 明確回傳 null 作為「沒有資料」的意義,此時應該保留 null 而不是覆寫。 |
若要保留 null,不要使用 ??,改用條件判斷或 ` |
| 使用在非空值的預設場景 | 有時候我們真的想把 0、'' 視為「需要預設」的情況。 |
仍可使用 ` |
最佳實踐
- 只在需要區分
null/undefined與其他 falsy 值時使用??。 - 搭配 Optional Chaining (
?.),先確保左側不會因屬性不存在拋錯。 - 保持運算子一致性:在同一行或同一段程式碼中,盡量避免同時使用
??、||、&&,以免混淆。 - 加上註解說明:尤其在多人協作的專案,標註「為何使用
??而非||」能減少未來的誤解。 - 測試所有可能的輸入:包括
null、undefined、0、''、false,確保行為符合預期。
實際應用場景
設定檔與環境變數
- 在 Node.js 中讀取
.env時,常會看到process.env.PORT ?? 3000,確保只在未定義時才使用預設埠號。
- 在 Node.js 中讀取
多語系文字的 fallback
- 讀取 i18n JSON 時,
messages[key] ?? messages['en-US']可在缺少翻譯時自動回退到英文。
- 讀取 i18n JSON 時,
第三方 API 的容錯
- 某些 API 只在資料缺失時回傳
null,使用??能直接提供前端所需的占位圖或文字。
- 某些 API 只在資料缺失時回傳
React/Vue 中的條件渲染
user?.avatar ?? '/img/default.png'讓 UI 不會因user為null而崩潰,同時保留空字串''作為合法值。
函式庫的 API 設計
- 公開函式接受選項物件時,內部使用
options.timeout ?? 5000,讓使用者能明確傳入0(表示「立即」)而不被預設值覆寫。
- 公開函式接受選項物件時,內部使用
總結
Nullish Coalescing Operator (??) 為 JavaScript 提供了一個精準且語意清晰的「空值備援」機制。相較於傳統的 ||,它只在左側為 null 或 undefined 時才會回傳右側值,讓 0、''、false 等合法的 falsy 值不會被意外覆寫。
透過本文的概念說明、實務範例與最佳實踐,你應該已經能在以下情境中自信使用 ??:
- 表單與 UI 的預設值
- API 回傳資料的容錯處理
- 設定檔與環境變數的合併
- 函式參數與解構賦值的安全預設
只要記住「只在需要區分 null/undefined 與其他 falsy 值時使用 ??,並避免與 &&、|| 混用而忘記加括號」,就能寫出更 可讀、可維護 的程式碼。未來在面對更複雜的資料流與多層物件結構時,?? 仍是你不可或缺的工具。祝你寫程式愉快,玩轉 Modern JS!