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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
|
---
title: 解構賦值
slug: Web/JavaScript/Reference/Operators/Destructuring_assignment
tags:
- Destructuring
- Destructuring_assignment
- ECMAScript 2015
- ES6
- JavaScript
- Language feature
- Operator
- 解構
translation_of: Web/JavaScript/Reference/Operators/Destructuring_assignment
---
<div>{{jsSidebar("Operators")}}</div>
<p><strong>解構賦值</strong> (Destructuring assignment) 語法是一種 JavaScript 運算式,可以把陣列或物件中的資料解開擷取成為獨立變數。</p>
<div>{{EmbedInteractiveExample("pages/js/expressions-destructuringassignment.html", "taller")}}</div>
<h2 id="語法">語法</h2>
<pre class="brush:js">let a, b, rest;
[a, b] = [10, 20];
console.log(a); // 10
console.log(b); // 20
[a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(a); // 10
console.log(b); // 20
console.log(rest); // [30, 40, 50]
({ a, b } = { a: 10, b: 20 });
console.log(a); // 10
console.log(b); // 20
// Stage 4(finished) proposal
({a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40});
console.log(a); // 10
console.log(b); // 20
console.log(rest); // {c: 30, d: 40}
</pre>
<h2 id="描述">描述</h2>
<p>物件與陣列運算式提供了簡單的方式,建立特定的資料組。</p>
<pre class="brush: js">const x = [1, 2, 3, 4, 5];</pre>
<p>解構賦值使用類似語法;不過在指定敘述式的左側,要宣告從來源變數接收解開值之變數。</p>
<pre class="brush: js">const x = [1, 2, 3, 4, 5];
const [y, z] = x;
console.log(y); // 1
console.log(z); // 2
</pre>
<p>Perl 和 Python 也有類似的語法和功能。</p>
<h2 id="陣列解構">陣列解構</h2>
<h3 id="基本變數指定敘述">基本變數指定敘述</h3>
<pre class="brush: js">const foo = ['one', 'two', 'three'];
const [red, yellow, green] = foo;
console.log(red); // "one"
console.log(yellow); // "two"
console.log(green); // "three"
</pre>
<h3 id="宣告指派分開敍述">宣告指派分開敍述</h3>
<p>變數可以在宣告式後,再透過解構賦值。</p>
<pre class="brush:js">let a, b;
[a, b] = [1, 2];
console.log(a); // 1
console.log(b); // 2
</pre>
<h3 id="預設值">預設值</h3>
<p>當解構來源陣列對應的元素是 undefined 時,變數可以被設定預設值。</p>
<pre class="brush: js">let a, b;
[a=5, b=7] = [1];
console.log(a); // 1
console.log(b); // 7
</pre>
<h3 id="變數交換">變數交換</h3>
<p>兩個變數可以透過一個解構指派式交換。</p>
<p>沒有解構指派式時,這需要一個暫存變數來達成(或者像某些低階語言的 <a class="external" href="https://en.wikipedia.org/wiki/XOR_swap_algorithm">XOR-swap trick</a>)。</p>
<pre class="brush:js">let a = 1;
let b = 3;
[a, b] = [b, a];
console.log(a); // 3
console.log(b); // 1
const arr = [1,2,3];
[arr[2], arr[1]] = [arr[1], arr[2]];
console.log(arr); // [1,3,2]
</pre>
<h3 id="解析自函式回傳的陣列">解析自函式回傳的陣列</h3>
<p>一直以來函式都可以回傳陣列,而解構指派式可以讓回傳的值更加簡潔。</p>
<p>在這個例子, <code>f()</code> 回傳 <code>[1, 2]</code> ,接著透過一個解構指派式解析。</p>
<pre class="brush:js">function f() {
return [1, 2];
}
let a, b;
[a, b] = f();
console.log(a); // 1
console.log(b); // 2
</pre>
<h3 id="忽略某些回傳值">忽略某些回傳值</h3>
<p>你可以忽略某些回傳值:</p>
<pre class="brush:js">function f() {
return [1, 2, 3];
}
const [a, , b] = f();
console.log(a); // 1
console.log(b); // 3
</pre>
<p>當然你也可以忽略全部回傳值:</p>
<pre class="brush:js">[,,] = f();
</pre>
<h3 id="把矩陣剩餘部分解構到一個變數">把矩陣剩餘部分解構到一個變數</h3>
<p>解構一個陣列時,你可以透過其餘元素(rest pattern)將來源剩下之元素指派到一個變數:</p>
<pre class="brush: js">const [a, ...b] = [1, 2, 3];
console.log(a); // 1
console.log(b); // [2, 3]</pre>
<p>要注意的是,當左邊函式裡使用其餘解構,同時使用結尾逗號,這樣會拋出例外 {{jsxref("SyntaxError")}} :</p>
<pre class="brush: js example-bad">const [a, ...b,] = [1, 2, 3];
// SyntaxError 語法錯誤: 其餘元素不可以跟隨結尾逗號
// 需要把其餘運算子放在最後的元素
</pre>
<h3 id="從正則運算式的比對結果取值">從正則運算式的比對結果取值</h3>
<p>當正則運算式的方法 <code><a href="/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec">exec()</a></code> 比對到一個值,其回傳陣列中的第一個值是相符的完整字串,後績的則是比對到正則運算式每組括號內的部分。當你沒需要利用第一個完整比對結果時,解構指派式讓你更簡單的取出後績元素。</p>
<pre class="brush:js">function parseProtocol(url) {
const parsedURL = /^(\w+)\:\/\/([^\/]+)\/(.*)$/.exec(url);
if (!parsedURL) {
return false;
}
console.log(parsedURL); // ["https://developer.mozilla.org/en-US/Web/JavaScript", "https", "developer.mozilla.org", "en-US/Web/JavaScript"]
const [, protocol, fullhost, fullpath] = parsedURL;
return protocol;
}
console.log(parseProtocol('https://developer.mozilla.org/en-US/Web/JavaScript')); // "https"
</pre>
<h2 id="物件解構">物件解構</h2>
<h3 id="基本指派式">基本指派式</h3>
<pre class="brush: js">const o = {p: 42, q: true};
const {p, q} = o;
console.log(p); // 42
console.log(q); // true
</pre>
<h3 id="無宣告指派">無宣告指派</h3>
<p>變數可以在宣告式後,再透過解構進行指派。</p>
<pre class="brush:js">let a, b;
({a, b} = {a:1, b:2});</pre>
<div class="note">
<p><strong>注意</strong>:當針對物件進行解構,而該句式沒有進行宣告時,指派式外必須加上括號 <code>( ... )</code> 。</p>
<p><code>{a, b} = {a: 1, b: 2}</code> 不是有效的獨立語法,因為左邊的 <code>{a, b}</code> 被視為程式碼區塊而非物件。</p>
<p>然而,<code>({a, b} = {a: 1, b: 2})</code> 是有效的,如同 <code>const {a, b} = {a: 1, b: 2}</code></p>
<p><code>( ... )</code> 表達式前句需要以分號結束,否則可能把上一句視為函式隨即執行。</p>
</div>
<h3 id="指派到新的變數名稱">指派到新的變數名稱</h3>
<p>物件中的屬性可以解構並擷取到名稱跟該屬性不一樣的變數。</p>
<pre class="brush: js">const o = {p: 42, q: true};
const {p: foo, q: bar} = o;
console.log(foo); // 42
console.log(bar); // true</pre>
<p>舉例來說, <code>const {p: foo} = o</code> 把物件 <code>o</code> 裡名為 <code>p</code> 的屬性解出並指派到一個名為 <code>foo</code> 的本地變數。</p>
<h3 id="預設值_2">預設值</h3>
<p>當解構物件中對應的值是 <code>undefined</code> 時,變數可以設定預設值。</p>
<pre class="brush: js">const {a = 10, b = 5} = {a: 3};
console.log(a); // 3
console.log(b); // 5</pre>
<h3 id="指定新的變數名稱及預設值">指定新的變數名稱及預設值</h3>
<p>屬性 1) 可以從物件中被解開,且被指定一個不同名稱的變數及 2) 同時指定一個預設值,在解開的值為 <code>undefined</code> 時使用。</p>
<pre class="brush: js">const {a:aa = 10, b:bb = 5} = {a: 3};
console.log(aa); // 3
console.log(bb); // 5
</pre>
<h3 id="從作為函式參數的物件中提出某屬性的值">從作為函式參數的物件中提出某屬性的值</h3>
<pre class="brush:js">const user = {
id: 42,
displayName: 'jdoe',
fullName: {
firstName: 'John',
lastName: 'Doe'
}
};
function userId({id}) {
return id;
}
function whois({displayName, fullName: {firstName: name}}) {
return `${displayName} is ${name}`;
}
console.log(userId(user)); // 42
console.log(whois(user)); // "jdoe is John"</pre>
<p>這樣從 user 物件中提出了 <code>id</code>, <code>displayName</code> 和 <code>firstName</code> 並且印出。</p>
<h3 id="設定函式參數的預設值">設定函式參數的預設值</h3>
<pre class="brush: js">function drawChart({size = 'big', coords = {x: 0, y: 0}, radius = 25} = {}) {
console.log(size, coords, radius);
// do some chart drawing
}
drawChart({
coords: {x: 18, y: 30},
radius: 30
});</pre>
<div class="note">
<p>在上述函式 <strong><code>drawChart</code></strong> 中,左方之解構式被指派到一個空物件: <code>{size = 'big', coords = {x: 0, y: 0}, radius = 25} = {}</code> 。你也可以略過填寫右方的指派式。不過,當你沒有使用右方指派式時,函式在呼叫時會找出最少一個參數。透過上述形式,你可以直接不使用參數的呼叫 <code><strong>drawChart()</strong></code> 。當你希望在呼叫這個函式時不傳送參數,這個設計會帶來方便。而另一個設計則能讓你確保函式必須傳上一個物件作為參數。</p>
</div>
<h3 id="巢狀物件或陣列的解構">巢狀物件或陣列的解構</h3>
<pre class="brush:js">const metadata = {
title: 'Scratchpad',
translations: [
{
locale: 'de',
localization_tags: [],
last_edit: '2014-04-14T08:43:37',
url: '/de/docs/Tools/Scratchpad',
title: 'JavaScript-Umgebung'
}
],
url: '/en-US/docs/Tools/Scratchpad'
};
let {
title: englishTitle, // rename
translations: [
{
title: localeTitle, // rename
},
],
} = metadata;
console.log(englishTitle); // "Scratchpad"
console.log(localeTitle); // "JavaScript-Umgebung"</pre>
<h3 id="循環取出的解構">循環取出的解構</h3>
<pre class="brush: js">const people = [
{
name: 'Mike Smith',
family: {
mother: 'Jane Smith',
father: 'Harry Smith',
sister: 'Samantha Smith'
},
age: 35
},
{
name: 'Tom Jones',
family: {
mother: 'Norah Jones',
father: 'Richard Jones',
brother: 'Howard Jones'
},
age: 25
}
];
for (const {name: n, family: {father: f}} of people) {
console.log('Name: ' + n + ', Father: ' + f);
}
// "Name: Mike Smith, Father: Harry Smith"
// "Name: Tom Jones, Father: Richard Jones"
</pre>
<h3 id="以物件演算屬性名稱解構">以物件演算屬性名稱解構</h3>
<p>物件演算屬性名稱(像是在 <a href="/zh-TW/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names">object literals</a>)可以在解構指派式使用。</p>
<pre class="brush: js">let key = 'z';
let {[key]: foo} = {z: 'bar'};
console.log(foo); // "bar"
</pre>
<h3 id="在物件解構時使用其餘變數">在物件解構時使用其餘變數</h3>
<p><a class="external external-icon" href="https://github.com/tc39/proposal-object-rest-spread">ECMAScript 中的其餘/展開屬性</a>在 proposal (stage 4) 新增了在解構式內使用<a href="/zh-TW/docs/Web/JavaScript/Reference/Functions/rest_parameters">其餘 (rest)</a> 語法的定義。其餘屬性可以收集解構式中沒有指定的屬性值。</p>
<pre class="brush: js">let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40}
a; // 10
b; // 20
rest; // { c: 30, d: 40 }</pre>
<h3 id="不符合_JavaScript_識別字的屬性名稱">不符合 JavaScript 識別字的屬性名稱</h3>
<p>解構賦值可以透過另一個符合 JavaScript <a href="/zh-TW/docs/Glossary/Identifier">識別字</a>的變數名稱來解出不符合識別字的屬性。</p>
<pre class="brush: js">const foo = { 'fizz-buzz': true };
const { 'fizz-buzz': fizzBuzz } = foo;
console.log(fizzBuzz); // "true"
</pre>
<h3 id="混合使用矩陣及物件解構">混合使用矩陣及物件解構</h3>
<p>矩陣及物件解構可以混合進行。與例來說,你只需要使用下列 <code>props</code> 矩陣中第三個元素之物件中的 <code>name</code> 屬性,你可以如下面的例子進行解構:</p>
<pre class="brush: js">const props = [
{ id: 1, name: 'Fizz'},
{ id: 2, name: 'Buzz'},
{ id: 3, name: 'FizzBuzz'}
];
const [,, { name }] = props;
console.log(name); // "FizzBuzz"
</pre>
<h3 id="物件解構時的原型鏈追溯">物件解構時的原型鏈追溯</h3>
<p>在進行物件解構時,如果一個屬性不在其當下存取,將會透過原型鏈 (prototype chain) 來進行追溯。</p>
<pre>let obj = {self: '123'};
obj.__proto__.prot = '456';
const {self, prot} = obj;
// self "123"
// prot "456"(Access to the prototype chain)</pre>
<h2 id="規範">規範</h2>
<table class="standard-table">
<tbody>
<tr>
<th scope="col">規範</th>
</tr>
<tr>
<td>{{SpecName('ESDraft', '#sec-destructuring-assignment', 'Destructuring assignment')}}</td>
</tr>
</tbody>
</table>
<h2 id="瀏覽器相容性">瀏覽器相容性</h2>
<div>
<p>{{Compat("javascript.operators.destructuring")}}</p>
</div>
<h2 id="參見">參見</h2>
<ul>
<li><a href="/zh-TW/docs/Web/JavaScript/Reference/Operators/Assignment_Operators">Assignment operators</a></li>
<li><a href="https://hacks.mozilla.org/2015/05/es6-in-depth-destructuring/">"ES6 in Depth: Destructuring" on hacks.mozilla.org</a></li>
</ul>
|