可选链接 (?.)

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

¥The optional chaining (?.) operator accesses an object's property or calls a function. If the object accessed or function called using this operator is undefined or null, the expression short circuits and evaluates to undefined instead of throwing an error.

Try it

语法

¥Syntax

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

描述

¥Description

?. 运算符类似于 . 链接运算符,不同之处在于,如果引用是 nullishnullundefined),则表达式会短路并返回值 undefined,而不是导致错误。当与函数调用一起使用时,如果给定函数不存在,则返回 undefined

¥The ?. operator is like the . chaining operator, except that instead of causing an error if a reference is nullish (null or undefined), the expression short-circuits with a return value of undefined. When used with function calls, it returns undefined if the given function does not exist.

当存在引用丢失的可能性时,这会导致访问链式属性时表达式更短、更简单。当无法保证需要哪些属性时,它在探索对象的内容时也很有帮助。

¥This results in shorter and simpler expressions when accessing chained properties when the possibility exists that a reference may be missing. It can also be helpful while exploring the content of an object when there's no known guarantee as to which properties are required.

例如,考虑具有嵌套结构的对象 obj。如果没有可选链接,查找深度嵌套的子属性需要验证之间的引用,例如:

¥For example, consider an object obj which has a nested structure. Without optional chaining, looking up a deeply-nested subproperty requires validating the references in between, such as:

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

在访问 obj.first.second 的值之前,确认 obj.first 的值是非 null(且非 undefined)。这样可以防止在未测试 obj.first 的情况下直接访问 obj.first.second 时出现的错误。

¥The value of obj.first is confirmed to be non-null (and non-undefined) before accessing the value of obj.first.second. This prevents the error that would occur if you accessed obj.first.second directly without testing obj.first.

这是 JavaScript 中的惯用模式,但当链很长时,它会变得冗长,而且不安全。例如,如果 obj.firstFalsy 值,而不是 nullundefined,例如 0,它仍然会短路并使 nestedProp 变成 0,这可能是不可取的。

¥This is an idiomatic pattern in JavaScript, but it gets verbose when the chain is long, and it's not safe. For example, if obj.first is a Falsy value that's not null or undefined, such as 0, it would still short-circuit and make nestedProp become 0, which may not be desirable.

但是,使用可选链接运算符 (?.),在尝试访问 obj.first.second 之前,你不必根据 obj.first 的状态显式测试和短路:

¥With the optional chaining operator (?.), however, you don't have to explicitly test and short-circuit based on the state of obj.first before trying to access obj.first.second:

js
const nestedProp = obj.first?.second;

通过使用 ?. 运算符而不仅仅是 .,JavaScript 知道在尝试访问 obj.first.second 之前隐式检查以确保 obj.first 不是 nullundefined。如果 obj.firstnullundefined,则表达式自动短路,返回 undefined

¥By using the ?. operator instead of just ., JavaScript knows to implicitly check to be sure obj.first is not null or undefined before attempting to access obj.first.second. If obj.first is null or undefined, the expression automatically short-circuits, returning undefined.

这与以下内容等效,只是临时变量实际上并未创建:

¥This is equivalent to the following, except that the temporary variable is in fact not created:

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

可选链接不能用于未声明的根对象,但可以用于值为 undefined 的根对象。

¥Optional chaining cannot be used on a non-declared root object, but can be used with a root object with value undefined.

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

与函数调用的可选链接

¥Optional chaining with function calls

当尝试调用可能不存在的方法时,可以使用可选链。例如,当使用的 API 中的方法可能不可用(由于实现的时间较长或用户设备上不可用的功能)时,这可能会很有帮助。

¥You can use optional chaining when attempting to call a method which may not exist. This can be helpful, for example, when using an API in which a method might be unavailable, either due to the age of the implementation or because of a feature which isn't available on the user's device.

在函数调用中使用可选链会导致表达式自动返回 undefined,而不是在找不到方法时抛出异常:

¥Using optional chaining with function calls causes the expression to automatically return undefined instead of throwing an exception if the method isn't found:

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

但是,如果存在具有此类名称的属性,但该属性不是函数,则使用 ?. 仍会引发 TypeError 异常 "someInterface.customMethod 不是函数"。

¥However, if there is a property with such a name which is not a function, using ?. will still raise a TypeError exception "someInterface.customMethod is not a function".

注意:如果 someInterface 本身是 nullundefined,则仍会引发 TypeError 异常 ("某些接口为空")。如果你预计 someInterface 本身可能是 nullundefined,那么你也必须在这个位置使用 ?.someInterface?.customMethod?.()

¥Note: If someInterface itself is null or undefined, a TypeError exception will still be raised ("someInterface is null"). If you expect that someInterface itself may be null or undefined, you have to use ?. at this position as well: someInterface?.customMethod?.().

eval?.() 是进入 间接评估 模式的最短途径。

¥eval?.() is the shortest way to enter indirect eval mode.

可选的表达式链接

¥Optional chaining with expressions

你还可以将可选链接运算符与 括号表示法 一起使用,它允许将表达式作为属性名称传递:

¥You can also use the optional chaining operator with bracket notation, which allows passing an expression as the property name:

js
const nestedProp = obj?.["prop" + "Name"];

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

¥This is particularly useful for arrays, since array indices must be accessed with square brackets.

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')"

无效可选链

¥Invalid optional chaining

尝试分配给可选链接表达式的结果是无效的:

¥It is invalid to try to assign to the result of an optional chaining expression:

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

模板文字标签 不能是可选链(参见 语法错误:标记模板不能与可选链一起使用):

¥Template literal tags cannot be an optional chain (see 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 表达式的构造函数不能是可选链(参见 语法错误:new 关键字不能与可选的链):

¥The constructor of new expressions cannot be an optional chain (see SyntaxError: new keyword cannot be used with an optional chain):

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

短路

¥Short-circuiting

当对表达式使用可选链时,如果左操作数是 nullundefined,则不会计算表达式。例如:

¥When using optional chaining with expressions, if the left operand is null or undefined, the expression will not be evaluated. For instance:

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

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

后续的属性访问也不会被评估。

¥Subsequent property accesses will not be evaluated either.

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

这相当于:

¥This is equivalent to:

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

然而,这种短路行为仅发生在连续的 "chain" 次属性访问中。如果你 group 链的一部分,则仍将评估后续属性访问。

¥However, this short-circuiting behavior only happens along one continuous "chain" of property accesses. If you group one part of the chain, then subsequent property accesses will still be evaluated.

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

这相当于:

¥This is equivalent to:

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

但未创建 temp 变量。

¥Except the temp variable isn't created.

示例

¥Examples

基本示例

¥Basic example

此示例在映射中查找成员 barname 属性值(当不存在此类成员时)。因此结果是 undefined

¥This example looks for the value of the name property for the member bar in a map when there is no such member. The result is therefore undefined.

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

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

处理可选的回调或事件处理程序

¥Dealing with optional callbacks or event handlers

如果你使用回调或从具有 解构赋值 的对象中获取方法,则可能有不存在的值,除非你测试了它们的存在,否则无法将其作为函数调用。使用 ?.,你可以避免这个额外的测试:

¥If you use callbacks or fetch methods from an object with a destructuring assignment, you may have non-existent values that you cannot call as functions unless you have tested their existence. Using ?., you can avoid this extra test:

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
  }
}

堆叠可选的链操作符

¥Stacking the optional chaining operator

对于嵌套结构,可以多次使用可选链:

¥With nested structures, it is possible to use optional chaining multiple times:

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

与空值合并运算符结合

¥Combining with the nullish coalescing operator

空值合并运算符 可以在可选链接之后使用,以便在未找到任何值时构建默认值:

¥The nullish coalescing operator may be used after optional chaining in order to build a default value when none was found:

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"

规范

Specification
ECMAScript Language Specification
# prod-OptionalExpression

¥Specifications

浏览器兼容性

BCD tables only load in the browser

¥Browser compatibility

也可以看看