可選鏈 (?.)

Baseline 已廣泛支援

此特性已經十分成熟,可在許多裝置和瀏覽器版本上使用。自 2020 年 7 月以來,它已在各大瀏覽器中可用。

可選鏈式運算子(?. 用於訪問物件的屬性或呼叫函式。如果使用此運算子訪問的物件或呼叫的函式是 undefinednull,表示式會短路並求值為 undefined,而不是丟擲錯誤。

試一試

const adventurer = {
  name: "Alice",
  cat: {
    name: "Dinah",
  },
};

const dogName = adventurer.dog?.name;
console.log(dogName);
// Expected output: undefined

console.log(adventurer.someNonExistentMethod?.());
// Expected output: undefined

語法

js
obj?.prop
obj?.[expr]
func?.(args)

描述

?. 運算子類似於 . 鏈式運算子,不同之處在於,如果引用是 nullishnullundefined),它不會引發錯誤,而是短路並返回 undefined。當用於函式呼叫時,如果給定函式不存在,它會返回 undefined

當訪問鏈式屬性時,如果可能存在引用缺失的情況,這會使表示式更短、更簡潔。當探索物件內容時,如果無法保證哪些屬性是必需的,它也很有幫助。

例如,考慮一個具有巢狀結構的 obj 物件。沒有可選鏈式運算子,查詢深層巢狀的子屬性需要驗證中間的引用,例如:

js
const nestedProp = obj.first && obj.first.second;

在訪問 obj.first.second 的值之前,會確認 obj.first 的值不是 null(也不是 undefined)。這可以防止在沒有測試 obj.first 的情況下直接訪問 obj.first.second 時發生的錯誤。

這在 JavaScript 中是一種慣用模式,但當鏈條很長時會變得冗長,並且不安全。例如,如果 obj.first 是一個不是 nullundefinedFalsy 值,例如 0,它仍然會短路並使 nestedProp 變為 0,這可能不是我們想要的。

然而,有了可選鏈式運算子(?.),在嘗試訪問 obj.first.second 之前,你無需根據 obj.first 的狀態進行顯式測試和短路。

js
const nestedProp = obj.first?.second;

透過使用 ?. 運算子而不是僅僅使用 .,JavaScript 知道在嘗試訪問 obj.first.second 之前隱式檢查以確保 obj.first 不是 nullundefined。如果 obj.firstnullundefined,表示式會自動短路,返回 undefined

這等效於以下內容,除了實際上沒有建立臨時變數:

js
const temp = obj.first;
const nestedProp =
  temp === null || temp === undefined ? undefined : temp.second;

可選鏈式運算子不能用於未宣告的根物件,但可以用於值為 undefined 的根物件。

js
undeclaredVar?.prop; // ReferenceError: undeclaredVar is not defined

函式呼叫的可選鏈式運算子

當你嘗試呼叫一個可能不存在的方法時,可以使用可選鏈式運算子。這會很有幫助,例如,在使用 API 時,某個方法可能由於實現版本或使用者裝置上缺少某個功能而不可用。

使用函式呼叫的可選鏈式運算子會導致表示式在找不到方法時自動返回 undefined,而不是丟擲異常:

js
const result = someInterface.customMethod?.();

然而,如果存在同名的屬性但它不是一個函式,使用 ?. 仍然會丟擲 TypeError 異常:“someInterface.customMethod is not a function”。

注意:如果 someInterface 本身是 nullundefined,仍然會丟擲 TypeError 異常(“someInterface is null”)。如果你預期 someInterface 本身可能為 nullundefined,你也必須在此位置使用 ?.someInterface?.customMethod?.()

eval?.() 是進入 間接 eval 模式的最短方式。

帶表示式的可選鏈式運算子

你還可以將可選鏈式運算子與 方括號表示法 一起使用,這允許將表示式作為屬性名傳遞:

js
const propName = "x";
const nestedProp = obj?.[propName];

這對於陣列特別有用,因為陣列索引必須用方括號訪問。

js
function printMagicIndex(arr) {
  console.log(arr?.[42]);
}

printMagicIndex([0, 1, 2, 3, 4, 5]); // undefined
printMagicIndex(); // undefined; if not using ?., this would throw an error: "Cannot read properties of undefined (reading '42')"

無效的可選鏈式運算子

嘗試對可選鏈式表示式的結果進行賦值是無效的:

js
const object = {};
object?.property = 1; // SyntaxError: Invalid left-hand side in assignment

模板字面量標籤 不能是可選鏈式運算子(參見 SyntaxError: 標記模板不能與可選鏈式運算子一起使用

js
String?.raw`Hello, world!`;
String.raw?.`Hello, world!`; // SyntaxError: Invalid tagged template on optional chain

new 表示式的建構函式不能是可選鏈式運算子(參見 SyntaxError: new 關鍵字不能與可選鏈式運算子一起使用

js
new Intl?.DateTimeFormat(); // SyntaxError: Invalid optional chain from new expression
new Map?.();

短路求值

當可選鏈式運算子與表示式一起使用時,如果左運算元是 nullundefined,則表示式將不會被求值。例如:

js
const potentiallyNullObj = null;
let x = 0;
const prop = potentiallyNullObj?.[x++];

console.log(x); // 0 as x was not incremented

後續的屬性訪問也不會被求值。

js
const potentiallyNullObj = null;
const prop = potentiallyNullObj?.a.b;
// This does not throw, because evaluation has already stopped at
// the first optional chain

這等價於

js
const potentiallyNullObj = null;
const prop =
  potentiallyNullObj === null || potentiallyNullObj === undefined
    ? undefined
    : potentiallyNullObj.a.b;

然而,這種短路行為只發生在一個連續的屬性訪問“鏈”上。如果你 分組 鏈條的一部分,那麼後續的屬性訪問仍將被求值。

js
const potentiallyNullObj = null;
const prop = (potentiallyNullObj?.a).b;
// TypeError: Cannot read properties of undefined (reading 'b')

這等價於

js
const potentiallyNullObj = null;
const temp = potentiallyNullObj?.a;
const prop = temp.b;

除了 temp 變數沒有被建立。

示例

基本示例

此示例在對映中查詢成員 CSSname 屬性值,但不存在這樣的成員。因此結果是 undefined

js
const myMap = new Map();
myMap.set("JS", { name: "Josh", desc: "I maintain things" });

const nameBar = myMap.get("CSS")?.name;

處理可選回撥或事件處理器

如果你使用回撥函式或從具有 解構 模式的物件中獲取方法,你可能會遇到不存在的值,除非你測試它們是否存在,否則你無法將它們作為函式呼叫。使用 ?.,你可以避免這種額外的測試:

js
// Code written without optional chaining
function doSomething(onContent, onError) {
  try {
    // Do something with the data
  } catch (err) {
    // Testing if onError really exists
    if (onError) {
      onError(err.message);
    }
  }
}
js
// Using optional chaining with function calls
function doSomething(onContent, onError) {
  try {
    // Do something with the data
  } catch (err) {
    onError?.(err.message); // No exception if onError is undefined
  }
}

可選鏈式運算子的堆疊

對於巢狀結構,可以多次使用可選鏈式運算子:

js
const customer = {
  name: "Carl",
  details: {
    age: 82,
    location: "Paradise Falls", // Detailed address is unknown
  },
};
const customerCity = customer.details?.address?.city;

// This also works with optional chaining function call
const customerName = customer.name?.getName?.(); // Method does not exist, customerName is undefined

與空值合併運算子結合使用

空值合併運算子 可以在可選鏈式運算子之後使用,以便在沒有找到值時構建預設值:

js
function printCustomerCity(customer) {
  const customerCity = customer?.city ?? "Unknown city";
  console.log(customerCity);
}

printCustomerCity({
  name: "Nathan",
  city: "Paris",
}); // "Paris"
printCustomerCity({
  name: "Carl",
  details: { age: 82 },
}); // "Unknown city"

規範

規範
ECMAScript® 2026 語言規範
# prod-OptionalExpression

瀏覽器相容性

另見