可选链 (?.)
可选链式操作符(?.) 用于访问对象的属性或调用函数。如果使用此操作符访问的对象或调用的函数是 undefined 或 null,表达式会短路并求值为 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
语法
obj?.prop
obj?.[expr]
func?.(args)
描述
?. 操作符类似于 . 链式操作符,不同之处在于,如果引用是 nullish(null 或 undefined),它不会引发错误,而是短路并返回 undefined。当用于函数调用时,如果给定函数不存在,它会返回 undefined。
当访问链式属性时,如果可能存在引用缺失的情况,这会使表达式更短、更简洁。当探索对象内容时,如果无法保证哪些属性是必需的,它也很有帮助。
例如,考虑一个具有嵌套结构的 obj 对象。没有可选链式操作符,查找深层嵌套的子属性需要验证中间的引用,例如:
const nestedProp = obj.first && obj.first.second;
在访问 obj.first.second 的值之前,会确认 obj.first 的值不是 null(也不是 undefined)。这可以防止在没有测试 obj.first 的情况下直接访问 obj.first.second 时发生的错误。
这在 JavaScript 中是一种惯用模式,但当链条很长时会变得冗长,并且不安全。例如,如果 obj.first 是一个不是 null 或 undefined 的 Falsy 值,例如 0,它仍然会短路并使 nestedProp 变为 0,这可能不是我们想要的。
然而,有了可选链式操作符(?.),在尝试访问 obj.first.second 之前,你无需根据 obj.first 的状态进行显式测试和短路。
const nestedProp = obj.first?.second;
通过使用 ?. 操作符而不是仅仅使用 .,JavaScript 知道在尝试访问 obj.first.second 之前隐式检查以确保 obj.first 不是 null 或 undefined。如果 obj.first 是 null 或 undefined,表达式会自动短路,返回 undefined。
这等效于以下内容,除了实际上没有创建临时变量:
const temp = obj.first;
const nestedProp =
temp === null || temp === undefined ? undefined : temp.second;
可选链式操作符不能用于未声明的根对象,但可以用于值为 undefined 的根对象。
undeclaredVar?.prop; // ReferenceError: undeclaredVar is not defined
函数调用的可选链式操作符
当你尝试调用一个可能不存在的方法时,可以使用可选链式操作符。这会很有帮助,例如,在使用 API 时,某个方法可能由于实现版本或用户设备上缺少某个功能而不可用。
使用函数调用的可选链式操作符会导致表达式在找不到方法时自动返回 undefined,而不是抛出异常:
const result = someInterface.customMethod?.();
然而,如果存在同名的属性但它不是一个函数,使用 ?. 仍然会抛出 TypeError 异常:“someInterface.customMethod is not a function”。
注意:如果 someInterface 本身是 null 或 undefined,仍然会抛出 TypeError 异常(“someInterface is null”)。如果你预期 someInterface 本身可能为 null 或 undefined,你也必须在此位置使用 ?.:someInterface?.customMethod?.()。
eval?.() 是进入 间接 eval 模式的最短方式。
带表达式的可选链式操作符
你还可以将可选链式操作符与 方括号表示法 一起使用,这允许将表达式作为属性名传递:
const propName = "x";
const nestedProp = obj?.[propName];
这对于数组特别有用,因为数组索引必须用方括号访问。
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')"
无效的可选链式操作符
尝试对可选链式表达式的结果进行赋值是无效的:
const object = {};
object?.property = 1; // SyntaxError: Invalid left-hand side in assignment
模板字面量标签 不能是可选链式操作符(参见 SyntaxError: 标记模板不能与可选链式操作符一起使用)
String?.raw`Hello, world!`;
String.raw?.`Hello, world!`; // SyntaxError: Invalid tagged template on optional chain
new 表达式的构造函数不能是可选链式操作符(参见 SyntaxError: new 关键字不能与可选链式操作符一起使用)
new Intl?.DateTimeFormat(); // SyntaxError: Invalid optional chain from new expression
new Map?.();
短路求值
当可选链式操作符与表达式一起使用时,如果左操作数是 null 或 undefined,则表达式将不会被求值。例如:
const potentiallyNullObj = null;
let x = 0;
const prop = potentiallyNullObj?.[x++];
console.log(x); // 0 as x was not incremented
后续的属性访问也不会被求值。
const potentiallyNullObj = null;
const prop = potentiallyNullObj?.a.b;
// This does not throw, because evaluation has already stopped at
// the first optional chain
这等价于
const potentiallyNullObj = null;
const prop =
potentiallyNullObj === null || potentiallyNullObj === undefined
? undefined
: potentiallyNullObj.a.b;
然而,这种短路行为只发生在一个连续的属性访问“链”上。如果你 分组 链条的一部分,那么后续的属性访问仍将被求值。
const potentiallyNullObj = null;
const prop = (potentiallyNullObj?.a).b;
// TypeError: Cannot read properties of undefined (reading 'b')
这等价于
const potentiallyNullObj = null;
const temp = potentiallyNullObj?.a;
const prop = temp.b;
除了 temp 变量没有被创建。
示例
基本示例
此示例在映射中查找成员 CSS 的 name 属性值,但不存在这样的成员。因此结果是 undefined。
const myMap = new Map();
myMap.set("JS", { name: "Josh", desc: "I maintain things" });
const nameBar = myMap.get("CSS")?.name;
处理可选回调或事件处理器
如果你使用回调函数或从具有 解构 模式的对象中获取方法,你可能会遇到不存在的值,除非你测试它们是否存在,否则你无法将它们作为函数调用。使用 ?.,你可以避免这种额外的测试:
// 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);
}
}
}
// 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
}
}
可选链式操作符的堆叠
对于嵌套结构,可以多次使用可选链式操作符:
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
与空值合并运算符结合使用
空值合并运算符 可以在可选链式操作符之后使用,以便在没有找到值时构建默认值:
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 |
浏览器兼容性
加载中…