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
|
---
title: Spread syntax (...)
slug: Web/JavaScript/Reference/Operators/Spread_syntax
translation_of: Web/JavaScript/Reference/Operators/Spread_syntax
---
<div>{{jsSidebar("Operators")}}</div>
<p><strong>展開運算子</strong>(<code>...</code>) 允許可迭代的陣列或字串展開成0到多個參數(如果是function的話)或是0到多個元素(如果是array或字組的話),或如果是物件的話則展開成0到多個key-value pair。</p>
<div>{{EmbedInteractiveExample("pages/js/expressions-spreadsyntax.html")}}</div>
<div class="hidden">The source for this interactive example is stored in a GitHub repository. If you'd like to contribute to the interactive examples project, please clone <a href="https://github.com/mdn/interactive-examples">https://github.com/mdn/interactive-examples</a> and send us a pull request.</div>
<h2 id="語法">語法</h2>
<p>用在呼叫函式時:</p>
<pre class="syntaxbox"><var>myFunction</var>(...<var>iterableObj</var>);
</pre>
<p>用在陣列或字串時:</p>
<pre class="syntaxbox">[...<var>iterableObj</var>, '4', 'five', 6];</pre>
<p>用在物件時(new in ECMAScript 2018):</p>
<pre class="syntaxbox">let <var>objClone</var> = { ...<var>obj</var> };</pre>
<h2 id="Rest_syntax_parameters">Rest syntax (parameters)</h2>
<p>Rest syntax looks exactly like spread syntax. In a way, rest syntax is the opposite of spread syntax. Spread syntax "expands" an array into its elements, while rest syntax collects multiple elements and "condenses" them into a single element. See {{jsxref("Functions/rest_parameters", "rest parameters", "", 1)}}.</p>
<h2 id="Examples">Examples</h2>
<h3 id="Spread_in_function_calls">Spread in function calls</h3>
<h4 id="Replace_apply">Replace apply()</h4>
<p>It is common to use {{jsxref("Function.prototype.apply()")}} in cases where you want to use the elements of an array as arguments to a function.</p>
<pre class="brush: js">function myFunction(x, y, z) { }
const args = [0, 1, 2];
myFunction.apply(null, args);</pre>
<p>With spread syntax the above can be written as:</p>
<pre class="brush: js">function myFunction(x, y, z) { }
const args = [0, 1, 2];
myFunction(...args);</pre>
<p>Any argument in the argument list can use spread syntax, and the spread syntax can be used multiple times.</p>
<pre class="brush: js">function myFunction(v, w, x, y, z) { }
const args = [0, 1];
myFunction(-1, ...args, 2, ...[3]);</pre>
<h4 id="Apply_for_new_operator">Apply for new operator</h4>
<p>When calling a constructor with {{jsxref("Operators/new", "new")}} it's not possible to <strong>directly</strong> use an array and <code>apply()</code> (<code>apply()</code> does a <code>[[Call]]</code> and not a <code>[[Construct]]</code>). However, an array can be easily used with <code>new</code> thanks to spread syntax:</p>
<pre class="brush: js">const dateFields = [1970, 0, 1]; // 1 Jan 1970
const d = new Date(...dateFields);
</pre>
<p>To use <code>new</code> with an array of parameters without spread syntax, you would have to do it <strong>indirectly</strong> through partial application:</p>
<pre class="brush: js">function applyAndNew(constructor, args) {
function partial () {
return constructor.apply(this, args);
};
if (typeof constructor.prototype === "object") {
partial.prototype = Object.create(constructor.prototype);
}
return partial;
}
function myConstructor () {
console.log("arguments.length: " + arguments.length);
console.log(arguments);
this.prop1="val1";
this.prop2="val2";
};
const myArguments = ["hi", "how", "are", "you", "mr", null];
const myConstructorWithArguments = applyAndNew(myConstructor, myArguments);
console.log(new myConstructorWithArguments);
// (internal log of myConstructor): arguments.length: 6
// (internal log of myConstructor): ["hi", "how", "are", "you", "mr", null]
// (log of "new myConstructorWithArguments"): {prop1: "val1", prop2: "val2"}</pre>
<h3 id="Spread_in_array_literals">Spread in array literals</h3>
<h4 id="A_more_powerful_array_literal">A more powerful array literal</h4>
<p>Without spread syntax, to create a new array using an existing array as one part of it, the array literal syntax is no longer sufficient and imperative code must be used instead using a combination of {{jsxref("Array.prototype.push", "push()")}}, {{jsxref("Array.prototype.splice", "splice()")}}, {{jsxref("Array.prototype.concat", "concat()")}}, etc. With spread syntax this becomes much more succinct:</p>
<pre class="brush: js">const parts = ['shoulders', 'knees'];
const lyrics = ['head', ...parts, 'and', 'toes'];
// ["head", "shoulders", "knees", "and", "toes"]
</pre>
<p>Just like spread for argument lists, <code>...</code> can be used anywhere in the array literal, and may be used more than once.</p>
<h4 id="Copy_an_array">Copy an array</h4>
<pre class="brush: js">const arr = [1, 2, 3];
const arr2 = [...arr]; // like arr.slice()
arr2.push(4);
// arr2 becomes [1, 2, 3, 4]
// arr remains unaffected
</pre>
<div class="blockIndicator note">
<p><strong>Note:</strong> Spread syntax effectively goes one level deep while copying an array. Therefore, it may be unsuitable for copying multidimensional arrays, as the following example shows. (The same is true with {{jsxref("Object.assign()")}} and spread syntax.)</p>
<pre class="brush: js example-bad">const a = [[1], [2], [3]];
const b = [...a];
b.shift().shift();
// 1
// Oh no! Now array 'a' is affected as well:
a
// [[], [2], [3]]
</pre>
</div>
<h4 id="A_better_way_to_concatenate_arrays">A better way to concatenate arrays</h4>
<p>{{jsxref("Array.prototype.concat()")}} is often used to concatenate an array to the end of an existing array. Without spread syntax, this is done as:</p>
<pre class="brush: js">const arr1 = [0, 1, 2];
const arr2 = [3, 4, 5];
// Append all items from arr2 onto arr1
arr1 = arr1.concat(arr2);</pre>
<p>With spread syntax this becomes:</p>
<pre class="brush: js">let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1 = [...arr1, ...arr2];
// arr1 is now [0, 1, 2, 3, 4, 5]
// Note: Not to use const otherwise, it will give TypeError (invalid assignment)
</pre>
<p>{{jsxref("Array.prototype.unshift()")}} is often used to insert an array of values at the start of an existing array. Without spread syntax, this is done as:</p>
<pre class="brush: js">const arr1 = [0, 1, 2];
const arr2 = [3, 4, 5];
// Prepend all items from arr2 onto arr1
Array.prototype.unshift.apply(arr1, arr2)
// arr1 is now [3, 4, 5, 0, 1, 2]</pre>
<p>With spread syntax, this becomes:</p>
<pre class="brush: js">let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1 = [...arr2, ...arr1];
// arr1 is now [3, 4, 5, 0, 1, 2]
</pre>
<div class="blockIndicator note">
<p><strong>Note:</strong> Unlike <code>unshift()</code>, this creates a new <code>arr1</code>, and does not modify the original <code>arr1</code> array in-place.</p>
</div>
<h3 id="Spread_in_object_literals">Spread in object literals</h3>
<p>The <a href="https://github.com/tc39/proposal-object-rest-spread">Rest/Spread Properties for ECMAScript</a> proposal (ES2018) added spread properties to {{jsxref("Operators/Object_initializer", "object literals", 1)}}. It copies own enumerable properties from a provided object onto a new object.</p>
<p>Shallow-cloning (excluding prototype) or merging of objects is now possible using a shorter syntax than {{jsxref("Object.assign()")}}.</p>
<pre class="brush: js">const obj1 = { foo: 'bar', x: 42 };
const obj2 = { foo: 'baz', y: 13 };
const clonedObj = { ...obj1 };
// Object { foo: "bar", x: 42 }
const mergedObj = { ...obj1, ...obj2 };
// Object { foo: "baz", x: 42, y: 13 }</pre>
<p>Note that {{jsxref("Object.assign()")}} triggers {{jsxref("Functions/set", "setters")}}, whereas spread syntax doesn't.</p>
<p>Note that you cannot replace or mimic the {{jsxref("Object.assign()")}} function:</p>
<pre class="brush: js">let obj1 = { foo: 'bar', x: 42 };
let obj2 = { foo: 'baz', y: 13 };
const merge = ( ...objects ) => ( { ...objects } );
let mergedObj1 = merge (obj1, obj2);
// Object { 0: { foo: 'bar', x: 42 }, 1: { foo: 'baz', y: 13 } }
let mergedObj2 = merge ({}, obj1, obj2);
// Object { 0: {}, 1: { foo: 'bar', x: 42 }, 2: { foo: 'baz', y: 13 } }</pre>
<p>In the above example, the spread syntax does not work as one might expect: it spreads an <em>array</em> of arguments into the object literal, due to the rest parameter.</p>
<h3 id="Only_for_iterables">Only for iterables</h3>
<p>Objects themselves are not iterable, but they become iterable when used in an Array, or with iterating functions such as <code>map()</code>, <code>reduce()</code>, and <code>assign()</code>. When merging 2 objects together with the spread operator, it is assumed another iterating function is used when the merging occurs.</p>
<p>Spread syntax (other than in the case of spread properties) can be applied only to <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator">iterable</a> objects:</p>
<pre class="brush: js">const obj = {'key1': 'value1'};
const array = [...obj]; // TypeError: obj is not iterable
</pre>
<h3 id="Spread_with_many_values">Spread with many values</h3>
<p>When using spread syntax for function calls, be aware of the possibility of exceeding the JavaScript engine's argument length limit. See {{jsxref("Function.prototype.apply", "apply()")}} for more details.</p>
<h2 id="Specifications">Specifications</h2>
<table class="standard-table">
<thead>
<tr>
<th scope="col">Specification</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{SpecName('ESDraft', '#sec-array-initializer', 'Array initializer')}}</td>
</tr>
<tr>
<td>{{SpecName('ESDraft', '#sec-object-initializer', 'Object initializer')}}</td>
</tr>
</tbody>
</table>
<h2 id="Browser_compatibility">Browser compatibility</h2>
<p>{{Compat("javascript.operators.spread")}}</p>
<h2 id="See_also">See also</h2>
<ul>
<li>{{jsxref("Functions/rest_parameters", "Rest parameters", "", 1)}} (also ‘<code>...</code>’)</li>
<li>{{jsxref("Function.prototype.apply()")}} (also ‘<code>...</code>’)</li>
</ul>
|