可选链接 (?.)
可选链接 (?.
) 运算符访问对象的属性或调用函数。如果使用此运算符访问的对象或调用的函数是 undefined
或 null
,则表达式会短路并计算为 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
语法
描述
¥Description
?.
运算符类似于 .
链接运算符,不同之处在于,如果引用是 nullish(null
或 undefined
),则表达式会短路并返回值 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:
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.first
是 Falsy 值,而不是 null
或 undefined
,例如 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
:
const nestedProp = obj.first?.second;
通过使用 ?.
运算符而不仅仅是 .
,JavaScript 知道在尝试访问 obj.first.second
之前隐式检查以确保 obj.first
不是 null
或 undefined
。如果 obj.first
是 null
或 undefined
,则表达式自动短路,返回 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:
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
.
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:
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
本身是null
或undefined
,则仍会引发TypeError
异常 ("某些接口为空")。如果你预计someInterface
本身可能是null
或undefined
,那么你也必须在这个位置使用?.
:someInterface?.customMethod?.()
。¥Note: If
someInterface
itself isnull
orundefined
, aTypeError
exception will still be raised ("someInterface is null"). If you expect thatsomeInterface
itself may benull
orundefined
, 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:
const nestedProp = obj?.["prop" + "Name"];
这对于数组特别有用,因为必须使用方括号访问数组索引。
¥This is particularly useful for arrays, since array indices must be accessed with square brackets.
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:
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):
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):
new Intl?.DateTimeFormat(); // SyntaxError: Invalid optional chain from new expression
new Map?.();
短路
¥Short-circuiting
当对表达式使用可选链时,如果左操作数是 null
或 undefined
,则不会计算表达式。例如:
¥When using optional chaining with expressions, if the left operand is null
or undefined
, the expression will not be evaluated. For instance:
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.
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:
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.
const potentiallyNullObj = null;
const prop = (potentiallyNullObj?.a).b;
// TypeError: Cannot read properties of undefined (reading 'b')
这相当于:
¥This is equivalent to:
const potentiallyNullObj = null;
const temp = potentiallyNullObj?.a;
const prop = temp.b;
但未创建 temp
变量。
¥Except the temp
variable isn't created.
示例
基本示例
¥Basic example
此示例在映射中查找成员 bar
的 name
属性值(当不存在此类成员时)。因此结果是 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
.
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:
// 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
}
}
堆叠可选的链操作符
¥Stacking the optional chaining operator
对于嵌套结构,可以多次使用可选链:
¥With nested structures, it is possible to use optional chaining multiple times:
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:
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 |
浏览器兼容性
BCD tables only load in the browser
也可以看看
¥See also