JavaScript ES6+ 新特性:Optional Chaining(?.)
簡介
在日常開發中,我們常常需要從深層的物件結構中取得資料,例如 user.profile.address.city。如果其中任意一層為 null 或 undefined,傳統的寫法會拋出 TypeError,導致程式碼必須寫滿大量的防呆檢查 (if (obj && obj.prop && ...))。
ES2020(亦稱 ES11)引入的 Optional Chaining(可選鏈)?.,讓這類防呆檢查變得簡潔且可讀。只要在屬性存取、函式呼叫或陣列存取前加上 ?.,若左側為 null 或 undefined,整個運算會直接回傳 undefined,不會拋錯。
對於 初學者,Optional Chaining 能降低因忘記檢查而產生的錯誤;對 中級開發者,則提供更乾淨的程式碼風格,提升維護性與可讀性。因此,掌握這個語法是使用 Modern JS 的必備功力。
核心概念
1. 基本語法
obj?.prop、obj?.[expr]、func?.(...args)、arr?.[index] 為四種常見形式。當左側的 參照值 為 null 或 undefined 時,結果直接是 undefined;否則會正常求值。
const user = {
name: "Alice",
profile: {
address: {
city: "Taipei"
}
}
};
console.log(user?.profile?.address?.city); // "Taipei"
console.log(user?.profile?.phone?.number); // undefined (不會拋錯)
重點:
?.只會檢查 左側 是否為null/undefined,不會對其他 falsy 值(如0、''、false)做特別處理。
2. 可選函式呼叫
const api = {
getUser: (id) => ({ id, name: "Bob" })
};
const result1 = api.getUser?.(123); // 正常呼叫,回傳物件
const result2 = api.fetchUser?.(123); // fetchUser 不存在,回傳 undefined
若函式本身不存在,使用 ?.() 能安全地「不呼叫」而不會產生 TypeError。
3. 可選陣列存取
const matrix = [
[1, 2],
[3, 4]
];
console.log(matrix?.[0]?.[1]); // 2
console.log(matrix?.[2]?.[0]); // undefined (matrix[2] 為 undefined)
?.[expr] 讓我們可以在動態索引或多層陣列時,同樣享有防呆保護。
4. 與 Nullish Coalescing (??) 結合
Optional Chaining 常與 Nullish Coalescing 搭配使用,提供「若結果為 undefined/ null 則使用預設值」的語意。
const config = {
timeout: 0,
retry: null
};
const timeout = config?.timeout ?? 5000; // 0 為有效值,結果 0
const retry = config?.retry ?? 3; // null 被視為 nullish,結果 3
5. 只在 左值 使用 ?.,不可在 右值
// 錯誤寫法:會 SyntaxError
// const value = (obj?.prop) ? true : false;
// 正確寫法
const value = obj?.prop ? true : false;
?. 必須緊貼在 屬性、方法或索引 前方,不能放在運算子或條件式的其他位置。
程式碼範例(實用示例)
範例 1:安全取得 API 回傳的深層資料
// 假設從遠端取得的資料結構不一定完整
function fetchUserInfo(id) {
// 模擬回傳值
return {
id,
profile: {
// address 可能不存在
// address: { city: "Kaohsiung" }
}
};
}
const data = fetchUserInfo(42);
// 使用 Optional Chaining 直接取得城市名稱,若不存在則回傳 '未知'
const city = data?.profile?.address?.city ?? "未知";
console.log(city); // "未知"
範例 2:在 React 組件中防止渲染錯誤
function UserCard({ user }) {
return (
<div className="card">
{/* 若 user.avatar 為 undefined,img src 會是 undefined,不會拋錯 */}
<img src={user?.avatar?.url} alt={user?.name ?? "匿名"} />
<h3>{user?.name ?? "未命名使用者"}</h3>
<p>{user?.profile?.bio ?? "沒有簡介"}</p>
</div>
);
}
範例 3:可選函式呼叫搭配事件處理
function onClick(event) {
// 若外部傳入的 callback 為 undefined,直接略過呼叫
const callback = event?.target?.onCustomClick;
callback?.(event); // 安全呼叫
}
範例 4:多層陣列資料的安全存取
const scores = [
[10, 20],
[30, 40, 50]
];
function getScore(row, col) {
// 若 row、col 超出範圍,回傳 -1 而不是拋錯
return scores?.[row]?.[col] ?? -1;
}
console.log(getScore(1, 2)); // 50
console.log(getScore(2, 0)); // -1
範例 5:結合解構賦值的防呆寫法
const response = {
data: {
user: {
name: "Carol"
}
}
};
// 使用可選鏈結取得 name,若不存在則給預設值
const { name = "匿名" } = response?.data?.user ?? {};
console.log(name); // "Carol"
常見陷阱與最佳實踐
| 陷阱 | 說明 | 建議的做法 |
|---|---|---|
| 誤用在左值 | obj?.prop = value 會產生 SyntaxError,因為 ?. 只能用於讀取。 |
若需要條件賦值,先檢查再賦值:if (obj) obj.prop = value; |
與 && 混用 |
仍習慣寫 obj && obj.prop?.sub,會造成冗餘且降低可讀性。 |
完全使用 ?.:obj?.prop?.sub。 |
忽略 null 與 undefined 之差 |
?. 僅檢查 null/undefined,若左側為 0、''、false,仍會正常求值。 |
依需求搭配 ?? 或顯式檢查 falsy 值。 |
| 過度使用 | 把所有屬性都包成 ?.,會隱藏資料結構錯誤,難以偵錯。 |
只在不確定的外部來源(API、使用者輸入)使用;內部已知結構則保留直接存取以利錯誤捕捉。 |
| 與 Proxy、Proxy Revocation | ?. 在 Proxy 物件上仍會觸發 get 捕獲器,可能產生副作用。 |
瞭解 Proxy 行為,必要時在 Proxy 內自行處理 undefined。 |
最佳實踐
- 明確搭配預設值:使用
??或三元運算子提供 fallback,避免傳遞undefined到後續流程。 - 保持可讀性:在同一行中不要混用太多
?.,適度斷行或使用中間變數提升可讀性。 - 工具支援:ESLint 的
no-unsafe-optional-chaining及prefer-optional-chain規則能協助統一寫法。 - 測試覆蓋:對外部 API 的回傳加入單元測試,確保在缺少欄位時仍能得到預期結果。
實際應用場景
- 前端 UI 渲染
- 從後端取得的 JSON 可能缺少某些欄位,使用
?.防止畫面崩潰。
- 從後端取得的 JSON 可能缺少某些欄位,使用
- Node.js 後端服務
- 處理多層設定檔 (
config.env?.db?.host) 時,避免因缺少環境變數拋錯。
- 處理多層設定檔 (
- 第三方 SDK 整合
- 某些 SDK 方法在特定版本才提供,使用
sdk?.method?.()保證向下相容。
- 某些 SDK 方法在特定版本才提供,使用
- React Hook 中的依賴
useEffect(() => { fetchData?.(); }, [fetchData]);,當fetchData為undefined時不會觸發錯誤。
- 資料驗證與轉換
- 在表單資料轉換為模型前,使用
form?.field?.value ?? ''取代繁瑣的if判斷。
- 在表單資料轉換為模型前,使用
總結
Optional Chaining (?.) 為 ES2020 引入的強大語法糖,讓深層物件存取變得安全且簡潔。它解決了傳統防呆寫法冗長、易錯的痛點,特別適合處理外部資料、API 回傳、動態屬性存取等情境。
掌握正確的使用方式、避免常見陷阱,並結合 Nullish Coalescing、ESLint 等工具,就能在日常開發中寫出更乾淨、可維護的程式碼。未來隨著 JavaScript 生態持續演進,Optional Chaining 也將成為撰寫 Modern JS 時的基本功,值得每位開發者深入了解與運用。祝你在程式之路上,因為「可選」而更從容!