可选链 (?.)

**可选链 (?.)** 运算符访问对象的属性或调用函数。如果使用此运算符访问的对象或调用的函数是 undefinednull,则表达式将短路并评估为 undefined,而不是抛出错误。

试试

语法

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

描述

?. 运算符类似于 . 链式运算符,不同之处在于,如果引用是 空值 (nullundefined),则表达式不会导致错误,而是会短路并返回 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 是一个非 nullundefined假值,例如 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 nestedProp = obj?.["prop" + "Name"];

这对数组特别有用,因为数组索引必须使用方括号访问。

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: tagged template cannot be used with optional chain)

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

new 表达式的构造函数不能是可选链(请参阅 SyntaxError: new keyword cannot be used with an optional chain)

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 变量不会被创建。

示例

基本示例

此示例在映射中查找成员 barname 属性的值,而该成员不存在。因此结果是 undefined

js
const myMap = new Map();
myMap.set("foo", { name: "baz", desc: "inga" });

const nameBar = myMap.get("bar")?.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 语言规范
# prod-OptionalExpression

浏览器兼容性

BCD 表格仅在浏览器中加载

另请参阅