1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
|
---
title: 可选链操作符
slug: Web/JavaScript/Reference/Operators/Optional_chaining
tags:
- '?.'
- JavaScript
- Optional chaining (?.)
- Reference
- 参考
- 可选链
- 语言特性
- 运算符
- 链式调用
translation_of: Web/JavaScript/Reference/Operators/Optional_chaining
original_slug: Web/JavaScript/Reference/Operators/可选链
---
<div>{{JSSidebar("Operators")}}</div>
<p><strong>可选链</strong>操作符( <strong><code>?.</code></strong> )允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。<code>?.</code> 操作符的功能类似于 <code>.</code> 链式操作符,不同之处在于,在引用为空(<a href="/zh-CN/docs/Glossary/nullish">nullish</a> ) ({{JSxRef("null")}} 或者 {{JSxRef("undefined")}}) 的情况下不会引起错误,该表达式短路返回值是 <code>undefined</code>。与函数调用一起使用时,如果给定的函数不存在,则返回 <code>undefined</code>。</p>
<p>当尝试访问可能不存在的对象属性时,可选链操作符将会使表达式更短、更简明。在探索一个对象的内容时,如果不能确定哪些属性必定存在,可选链操作符也是很有帮助的。</p>
<div>{{EmbedInteractiveExample("pages/js/expressions-optionalchainingoperator.html", "taller")}}</div>
<h2 id="语法">语法</h2>
<pre class="syntaxbox notranslate"><var>obj</var>?.<var>prop</var>
<var>obj</var>?.[<var>expr</var>]
<var>arr</var>?.[<var>index</var>]
<var>func</var>?.(<var>args</var>)
</pre>
<h2 id="描述">描述</h2>
<p>通过连接的对象的引用或函数可能是 <code>undefined</code> 或 <code>null</code> 时,可选链操作符提供了一种方法来简化被连接对象的值访问。</p>
<p>比如,思考一个存在嵌套结构的对象 <code>obj</code>。不使用可选链的话,查找一个深度嵌套的子属性时,需要验证之间的引用,例如:</p>
<pre class="brush: js notranslate">let nestedProp = obj.first && obj.first.second;</pre>
<p>为了避免报错,在访问<code>obj.first.second</code>之前,要保证 <code>obj.first</code> 的值既不是 <code>null</code>,也不是 <code>undefined</code>。如果只是直接访问 <code>obj.first.second</code>,而不对 <code>obj.first</code> 进行校验,则有可能抛出错误。</p>
<p>有了可选链操作符(<code>?.</code>),在访问 <code>obj.first.second</code> 之前,不再需要明确地校验 <code>obj.first</code> 的状态,再并用短路计算获取最终结果:</p>
<pre class="brush: js notranslate">let nestedProp = obj.first?.second;</pre>
<p>通过使用 <code>?.</code> 操作符取代 <code>.</code> 操作符,JavaScript 会在尝试访问 <code>obj.first.second</code> 之前,先隐式地检查并确定 <code>obj.first</code> 既不是 <code>null</code> 也不是 <code>undefined</code>。如果<code>obj.first </code>是 <code>null</code> 或者 <code>undefined</code>,表达式将会短路计算直接返回 <code>undefined</code>。</p>
<p>这等价于以下表达式,但实际上没有创建临时变量:</p>
<pre class="brush: js notranslate">let temp = obj.first;
let nestedProp = ((temp === null || temp === undefined) ? undefined : temp.second);</pre>
<h3 id="可选链与函数调用">可选链与函数调用</h3>
<p>当尝试调用一个可能不存在的方法时也可以使用可选链。这将是很有帮助的,比如,当使用一个API的方法可能不可用时,要么因为实现的版本问题要么因为当前用户的设备不支持该功能。</p>
<p>函数调用时如果被调用的方法不存在,使用可选链可以使表达式自动返回<code>undefined</code>而不是抛出一个异常。</p>
<pre class="brush: js notranslate">let result = someInterface.customMethod?.();</pre>
<div class="blockIndicator note">
<p><strong>注意:</strong> 如果存在一个属性名且不是函数, 使用 <code>?.</code> 仍然会产生一个 {{JSxRef("TypeError")}} 异常 (<code>x.y</code><code> is not a function</code>).</p>
</div>
<div class="blockIndicator note">
<p><strong>注意:</strong> 如果 <code>someInterface</code> 自身是 <code>null</code> 或者 <code>undefined</code> ,异常 {{JSxRef("TypeError")}} 仍会被抛出 <code>someInterface is null</code> 如果你希望允许 <code>someInterface</code> 也为 <code>null</code> 或者 <code>undefined</code> ,那么你需要像这样写 <code>someInterface?.customMethod?.()</code></p>
</div>
<h4 id="处理可选的回调函数或者事件处理器">处理可选的回调函数或者事件处理器</h4>
<p>如果使用<a href="/zh-CN/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment">解构赋值</a>来解构的一个对象的回调函数或 fetch 方法,你可能得到不能当做函数直接调用的不存在的值,除非你已经校验了他们的存在性。使用<code>?.</code>的你可以忽略这些额外的校验:</p>
<pre class="brush: js notranslate">// ES2019的写法
function doSomething(onContent, onError) {
try {
// ... do something with the data
}
catch (err) {
if (onError) { // 校验onError是否真的存在
onError(err.message);
}
}
}
</pre>
<pre class="brush: js notranslate">// 使用可选链进行函数调用
function doSomething(onContent, onError) {
try {
// ... do something with the data
}
catch (err) {
onError?.(err.message); // 如果onError是undefined也不会有异常
}
}
</pre>
<h3 id="可选链和表达式">可选链和表达式</h3>
<p>当使用<a href="/zh-CN/docs/Web/JavaScript/Reference/Operators/Property_Accessors#方括号表示法">方括号与属性名</a>的形式来访问属性时,你也可以使用可选链操作符:</p>
<pre class="brush: js notranslate">let nestedProp = obj?.['prop' + 'Name'];</pre>
<h3 id="可选链不能用于赋值">可选链不能用于赋值</h3>
<pre class="notranslate"><code>let object = {};
object?.property = 1; // Uncaught SyntaxError: Invalid left-hand side in assignment</code></pre>
<h3 id="可选链访问数组元素">可选链访问数组元素</h3>
<pre class="notranslate">let arrayItem = arr?.[42];</pre>
<h2 id="例子">例子</h2>
<h3 id="基本例子">基本例子</h3>
<p>如下的例子在一个不含 <code>bar</code> 成员的 Map 中查找 <code>bar</code> 成员的 <code>name</code> 属性,因此结果是 <code>undefined</code>。</p>
<pre class="brush: js notranslate">let myMap = new Map();
myMap.set("foo", {name: "baz", desc: "inga"});
let nameBar = myMap.get("bar")?.name;</pre>
<h3 id="短路计算">短路计算</h3>
<p>当在表达式中使用可选链时,如果左操作数是 <code>null</code> 或 <code>undefined</code>,表达式将不会被计算,例如:</p>
<pre class="brush: js notranslate">let potentiallyNullObj = null;
let x = 0;
let prop = potentiallyNullObj?.[x++];
console.log(x); // x 将不会被递增,依旧输出 0
</pre>
<h3 id="连用可选链操作符">连用可选链操作符</h3>
<p>可以连续使用可选链读取多层嵌套结构:</p>
<pre class="brush: js notranslate">let customer = {
name: "Carl",
details: {
age: 82,
location: "Paradise Falls" // details 的 address 属性未有定义
}
};
let customerCity = customer.details?.address?.city;
// … 可选链也可以和函数调用一起使用
let duration = vacations.trip?.getTime?.();
</pre>
<h3 id="使用空值合并操作符">使用空值合并操作符</h3>
<p>{{JSxRef("Operators/Nullish_Coalescing_Operator", "空值合并操作符")}}可以在使用可选链时设置一个默认值:</p>
<pre class="brush: js notranslate"><code>let customer = {
name: "Carl",
details: { age: 82 }
};
let customerCity = customer?.city ?? "暗之城";
console.log(customerCity); // “暗之城”</code></pre>
<h2 id="规范">规范</h2>
<table class="standard-table">
<thead>
<tr>
<th scope="col">规范</th>
<th scope="col">状态</th>
<th scope="col">备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{SpecName('ESDraft', '#prod-OptionalExpression', 'optional expression')}}</td>
<td>Stage 4</td>
<td></td>
</tr>
</tbody>
</table>
<h2 id="浏览器兼容性">浏览器兼容性</h2>
<p>{{Compat("javascript.operators.optional_chaining")}}</p>
<h2 id="参见">参见</h2>
<ul>
<li>{{JSxRef("Operators/Nullish_Coalescing_Operator", "空值合并操作符")}}</li>
<li><a href="https://github.com/tc39/proposals">TC39 提案</a></li>
</ul>
|