Skip to content

Commit 4af0d7f

Browse files
committed
Minor spread syntax rewrite
1 parent a71d5f9 commit 4af0d7f

1 file changed

Lines changed: 62 additions & 150 deletions

File tree

  • files/en-us/web/javascript/reference/operators/spread_syntax

files/en-us/web/javascript/reference/operators/spread_syntax/index.md

Lines changed: 62 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -11,79 +11,45 @@ browser-compat: javascript.operators.spread
1111
---
1212
{{jsSidebar("Operators")}}
1313

14-
**Spread syntax** (`...`) allows an iterable such as an array
15-
expression or string to be expanded in places where zero or more arguments (for
16-
function calls) or elements (for array literals) are expected, or an object expression
17-
to be expanded in places where zero or more key-value pairs (for object literals) are
18-
expected.
14+
**Spread syntax** (`...`) allows an iterable, such as an array or string, to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected. In an object literal, the spread syntax enumerates the properties of an object and adds the key-value pairs to the object being created.
1915

20-
{{EmbedInteractiveExample("pages/js/expressions-spreadsyntax.html")}}
21-
22-
## Description
23-
24-
Spread syntax can be used when all elements from an object or array need to be included
25-
in a list of some kind.
26-
27-
In the above example, the defined function takes `x`, `y`, and
28-
`z` as arguments and returns the sum of these values. An array value is also
29-
defined.
30-
31-
When we invoke the function, we pass it all the values in the array using the spread
32-
syntax and the array name — `...numbers`.
33-
34-
If the array contained more than three numbers, e.g. `[1, 2, 3, 4]`, then it
35-
would still work fine, except that all four would be passed, but only the first three
36-
would be used unless you added more arguments to the function, e.g.:
37-
38-
```js
39-
function sum(x, y, z, n) {
40-
return x + y + z + n;
41-
}
42-
```
16+
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 [rest parameters](/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters) and [rest property](/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#rest_property).
4317

44-
The above example is somewhat rigid; the real value in spread syntax is that it works
45-
with the same value, no matter how many elements are contained in the object, array,
46-
etc.
18+
{{EmbedInteractiveExample("pages/js/expressions-spreadsyntax.html")}}
4719

48-
It is commonly used when you want to add a new item to a local data store, or display
49-
all stored items plus a new addition. A very simple version of this kind of action
50-
could look like so:
20+
## Syntax
5121

5222
```js
53-
let numberStore = [0, 1, 2];
54-
let newNumber = 12;
55-
numberStore = [...numberStore, newNumber];
23+
myFunction(a, ...iterableObj, b)
24+
[1, ...iterableObj, '4', 'five', 6]
25+
({ ...obj, key: 'value' })
5626
```
5727

58-
In the above example you can rerun the last line as many times as you like, to keep
59-
adding an additional 12 to the end of the array.
28+
## Description
6029

61-
## Syntax
30+
Spread syntax can be used when all elements from an object or array need to be included in a new array or object, or should be applied one-by-one in a function call's arguments list. There are three distinct places that accept the spread syntax:
6231

63-
For function calls:
32+
- [Function arguments](#spread_in_function_calls) list (`myFunction(a, ...iterableObj, b)`)
33+
- [Array literals](#spread_in_array_literals) (`[1, ...iterableObj, '4', 'five', 6]`)
34+
- [Object literals](#spread_in_object_literals) (`{ ...obj, key: 'value' }`)
6435

65-
```js
66-
myFunction(...iterableObj); // pass all elements of iterableObj as arguments to function myFunction
67-
```
36+
Although the syntax looks the same, they come with slightly different semantics.
6837

69-
For array literals:
38+
Only [iterable](/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) objects, like {{jsxref("Array")}}, can be spread in array and function parameters. Many objects are not iterable, including all [plain objects](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) that lack a [`Symbol.iterator`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator) method:
7039

71-
```js
72-
[...iterableObj, '4', 'five', 6]; // combine two arrays by inserting all elements from iterableObj
40+
```js example-bad
41+
const obj = { key1: 'value1' };
42+
const array = [...obj]; // TypeError: obj is not iterable
7343
```
7444

75-
For object literals (new in ECMAScript 2018):
45+
On the other hand, spreading in object literals [enumerates](/en-US/docs/Web/JavaScript/Enumerability_and_ownership_of_properties#traversing_object_properties) the own properties of the object. For typical arrays, all indices are enumerable own properties, so arrays can be spread into objects.
7646

7747
```js
78-
let objClone = { ...obj }; // pass all key:value pairs from an object
48+
const array = [1, 2, 3];
49+
const obj = { ...array }; // { 0: 1, 1: 2, 2: 3 }
7950
```
8051

81-
## Rest syntax (parameters)
82-
83-
Rest syntax looks exactly like spread syntax. In a way, rest syntax is the opposite of
84-
spread syntax. Spread syntax "expands" an array into its elements, while rest syntax
85-
collects multiple elements and "condenses" them into a single element. See
86-
{{jsxref("Functions/rest_parameters", "rest parameters", "", 1)}}.
52+
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()")}} for more details.
8753

8854
## Examples
8955

@@ -95,68 +61,35 @@ It is common to use {{jsxref("Function.prototype.apply()")}} in cases where you
9561
use the elements of an array as arguments to a function.
9662

9763
```js
98-
function myFunction(x, y, z) { }
99-
let args = [0, 1, 2];
64+
function myFunction(x, y, z) {}
65+
const args = [0, 1, 2];
10066
myFunction.apply(null, args);
10167
```
10268

10369
With spread syntax the above can be written as:
10470

10571
```js
106-
function myFunction(x, y, z) { }
107-
let args = [0, 1, 2];
72+
function myFunction(x, y, z) {}
73+
const args = [0, 1, 2];
10874
myFunction(...args);
10975
```
11076

11177
Any argument in the argument list can use spread syntax, and the spread syntax can be
11278
used multiple times.
11379

11480
```js
115-
function myFunction(v, w, x, y, z) { }
116-
let args = [0, 1];
81+
function myFunction(v, w, x, y, z) {}
82+
const args = [0, 1];
11783
myFunction(-1, ...args, 2, ...[3]);
11884
```
11985

12086
#### Apply for new operator
12187

122-
When calling a constructor with {{jsxref("Operators/new", "new")}} it's not possible to
123-
**directly** use an array and `apply()` (`apply()`
124-
does a `[[Call]]` and not a `[[Construct]]`). However, an array
125-
can be easily used with `new` thanks to spread syntax:
126-
127-
```js
128-
let dateFields = [1970, 0, 1]; // 1 Jan 1970
129-
let d = new Date(...dateFields);
130-
```
131-
132-
To use `new` with an array of parameters without spread syntax, you would
133-
have to do it **indirectly** through partial application:
88+
When calling a constructor with {{jsxref("Operators/new", "new")}}, it's not possible to **directly** use an array and `apply()`, because `apply()` _calls_ the target function instead of _constructing_ it, which means, among other things, that [`new.target`](/en-US/docs/Web/JavaScript/Reference/Operators/new.target) will be `undefined`. However, an array can be easily used with `new` thanks to spread syntax:
13489

13590
```js
136-
function applyAndNew(constructor, args) {
137-
function partial () {
138-
return constructor.apply(this, args);
139-
};
140-
if (typeof constructor.prototype === "object") {
141-
partial.prototype = Object.create(constructor.prototype);
142-
}
143-
return partial;
144-
}
145-
146-
function myConstructor () {
147-
console.log("arguments.length: " + arguments.length);
148-
console.log(arguments);
149-
this.prop1="val1";
150-
this.prop2="val2";
151-
};
152-
153-
let myArguments = ["hi", "how", "are", "you", "mr", null];
154-
let myConstructorWithArguments = applyAndNew(myConstructor, myArguments);
155-
156-
console.log(new myConstructorWithArguments);
157-
// (internal log of myConstructor): arguments.length: 6
158-
// (internal log of myConstructor): ["hi", "how", "are", "you", "mr", null]
159-
// (log of "new myConstructorWithArguments"): {prop1: "val1", prop2: "val2"}
91+
const dateFields = [1970, 0, 1]; // 1 Jan 1970
92+
const d = new Date(...dateFields);
16093
```
16194

16295
### Spread in array literals
@@ -170,8 +103,8 @@ instead using a combination of {{jsxref("Array.prototype.push", "push()")}},
170103
"concat()")}}, etc. With spread syntax this becomes much more succinct:
171104

172105
```js
173-
let parts = ['shoulders', 'knees'];
174-
let lyrics = ['head', ...parts, 'and', 'toes'];
106+
const parts = ['shoulders', 'knees'];
107+
const lyrics = ['head', ...parts, 'and', 'toes'];
175108
// ["head", "shoulders", "knees", "and", "toes"]
176109
```
177110

@@ -181,22 +114,19 @@ literal, and may be used more than once.
181114
#### Copy an array
182115

183116
```js
184-
let arr = [1, 2, 3];
185-
let arr2 = [...arr]; // like arr.slice()
117+
const arr = [1, 2, 3];
118+
const arr2 = [...arr]; // like arr.slice()
186119

187120
arr2.push(4);
188121
// arr2 becomes [1, 2, 3, 4]
189122
// arr remains unaffected
190123
```
191124

192-
> **Note:** Spread syntax effectively goes one level deep while copying
193-
> an array. Therefore, it may be unsuitable for copying multidimensional arrays, as
194-
> the following example shows. (The same is true with {{jsxref("Object.assign()")}}
195-
> and spread syntax.)
125+
> **Note:** Spread syntax effectively goes one level deep while copying an array. Therefore, it may be unsuitable for copying multidimensional arrays. The same is true with {{jsxref("Object.assign()")}} — no native operation in JavaScript does a deep clone.
196126
>
197127
> ```js example-bad
198-
> let a = [[1], [2], [3]];
199-
> let b = [...a];
128+
> const a = [[1], [2], [3]];
129+
> const b = [...a];
200130
>
201131
> b.shift().shift();
202132
> // 1
@@ -213,32 +143,31 @@ of an existing array. Without spread syntax, this is done as:
213143
214144
```js
215145
let arr1 = [0, 1, 2];
216-
let arr2 = [3, 4, 5];
146+
const arr2 = [3, 4, 5];
217147
218-
// Append all items from arr2 onto arr1
148+
// Append all items from arr2 onto arr1
219149
arr1 = arr1.concat(arr2);
220150
```
221151
222152
With spread syntax this becomes:
223153

224154
```js
225155
let arr1 = [0, 1, 2];
226-
let arr2 = [3, 4, 5];
156+
const arr2 = [3, 4, 5];
227157

228158
arr1 = [...arr1, ...arr2];
229-
// arr1 is now [0, 1, 2, 3, 4, 5]
230-
// Note: Not to use const otherwise, it will give TypeError (invalid assignment)
159+
// arr1 is now [0, 1, 2, 3, 4, 5]
231160
```
232161

233162
{{jsxref("Array.prototype.unshift()")}} is often used to insert an array of values at
234163
the start of an existing array. Without spread syntax, this is done as:
235164

236165
```js
237-
let arr1 = [0, 1, 2];
238-
let arr2 = [3, 4, 5];
166+
const arr1 = [0, 1, 2];
167+
const arr2 = [3, 4, 5];
239168

240169
// Prepend all items from arr2 onto arr1
241-
Array.prototype.unshift.apply(arr1, arr2)
170+
Array.prototype.unshift.apply(arr1, arr2);
242171

243172
// arr1 is now [3, 4, 5, 0, 1, 2]
244173
```
@@ -247,38 +176,39 @@ With spread syntax, this becomes:
247176

248177
```js
249178
let arr1 = [0, 1, 2];
250-
let arr2 = [3, 4, 5];
179+
const arr2 = [3, 4, 5];
251180

252181
arr1 = [...arr2, ...arr1];
253182
// arr1 is now [3, 4, 5, 0, 1, 2]
254183
```
255184

256-
> **Note:** Unlike `unshift()`, this creates a new
257-
> `arr1`, and does not modify the original `arr1` array
258-
> in-place.
185+
> **Note:** Unlike `unshift()`, this creates a new `arr1`, instead of modifying the original `arr1` array in-place.
259186
260187
### Spread in object literals
261188

262-
The [Rest/Spread Properties for ECMAScript](https://github.com/tc39/proposal-object-rest-spread) proposal (ES2018)
263-
added spread properties to {{jsxref("Operators/Object_initializer", "object literals", 1)}}.
264-
It copies own enumerable properties from a provided object onto a new object.
265-
266-
Shallow-cloning (excluding prototype) or merging of objects is now possible using a
267-
shorter syntax than {{jsxref("Object.assign()")}}.
189+
Shallow-cloning (excluding prototype) or merging of objects is possible using a shorter syntax than {{jsxref("Object.assign()")}}.
268190

269191
```js
270-
let obj1 = { foo: 'bar', x: 42 };
271-
let obj2 = { foo: 'baz', y: 13 };
192+
const obj1 = { foo: 'bar', x: 42 };
193+
const obj2 = { foo: 'baz', y: 13 };
272194

273-
let clonedObj = { ...obj1 };
195+
const clonedObj = { ...obj1 };
274196
// Object { foo: "bar", x: 42 }
275197

276-
let mergedObj = { ...obj1, ...obj2 };
198+
const mergedObj = { ...obj1, ...obj2 };
277199
// Object { foo: "baz", x: 42, y: 13 }
278200
```
279201

280202
Note that {{jsxref("Object.assign()")}} can be used to mutate an object, whereas spread syntax can't.
281203

204+
```js
205+
const obj1 = { foo: 'bar', x: 42 };
206+
Object.assign(obj1, { x: 1337 });
207+
console.log(obj1); // Object { foo: "bar", x: 1337 }
208+
```
209+
210+
In addition, {{jsxref("Object.assign()")}} triggers setters on the target object, whereas spread syntax does not.
211+
282212
```js
283213
const objectAssign = Object.assign({ set foo(val) { console.log(val); } }, { foo: 1 });
284214
// Logs "1"; objectAssign.foo is still the original setter
@@ -312,25 +242,6 @@ const mergedObj1 = merge(obj1, obj2);
312242
// Object { foo: 'baz', x: 42, y: 13 }
313243
```
314244

315-
### Only for iterables
316-
317-
Spread syntax (other than in the case of spread properties) can only be applied to [iterable](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator)
318-
objects like {{jsxref("Array")}}, or with iterating functions such as `map()`, `reduce()`, and `assign()`.
319-
320-
Many objects are not iterable, including {{JSxRef("Object")}}:
321-
322-
```js
323-
let obj = {'key1': 'value1'};
324-
let array = [...obj]; // TypeError: obj is not iterable
325-
```
326-
327-
To use spread syntax with these objects, you will need to provide an iterator function.
328-
329-
### Spread with many values
330-
331-
When using spread syntax for function calls, be aware of the possibility of exceeding the JavaScript engine's argument length limit.
332-
See {{jsxref("Function.prototype.apply", "apply()")}} for more details.
333-
334245
## Specifications
335246

336247
{{Specifications}}
@@ -341,5 +252,6 @@ See {{jsxref("Function.prototype.apply", "apply()")}} for more details.
341252

342253
## See also
343254

344-
- {{jsxref("Functions/rest_parameters", "Rest parameters", "", 1)}} (also '`...`')
345-
- {{jsxref("Function.prototype.apply()")}} (also '`...`')
255+
- [Rest parameters](/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters)
256+
- [Rest property](/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#rest_property)
257+
- {{jsxref("Function.prototype.apply()")}}

0 commit comments

Comments
 (0)