传播句法
(
...
) allows an iterable such as an array expression or string to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected, or an object expression to be expanded in places where zero or more key-value pairs (for object literals) are expected.
对于函数调用:
myFunction(...iterableObj);
对于数组文字或字符串:
[...iterableObj, '4', 'five', 6];
对于对象文字 (ECMAScript 2018 新增):
let objClone = { ...obj };
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 .
It is common to use
Function.prototype.apply()
in cases where you want to use the elements of an array as arguments to a function.
function myFunction(x, y, z) { }
const args = [0, 1, 2];
myFunction.apply(null, args);
With spread syntax the above can be written as:
function myFunction(x, y, z) { }
const args = [0, 1, 2];
myFunction(...args);
Any argument in the argument list can use spread syntax, and the spread syntax can be used multiple times.
function myFunction(v, w, x, y, z) { }
const args = [0, 1];
myFunction(-1, ...args, 2, ...[3]);
When calling a constructor with
new
it's not possible to
directly
use an array and
apply()
(
apply()
does a
[[Call]]
and not a
[[Construct]]
). However, an array can be easily used with
new
thanks to spread syntax:
const dateFields = [1970, 0, 1]; // 1 Jan 1970 const d = new Date(...dateFields);
要使用
new
with an array of parameters without spread syntax, you would have to do it
indirectly
through partial application:
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"}
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
push()
,
splice()
,
concat()
, etc. With spread syntax this becomes much more succinct:
const parts = ['shoulders', 'knees']; const lyrics = ['head', ...parts, 'and', 'toes']; // ["head", "shoulders", "knees", "and", "toes"]
Just like spread for argument lists,
...
can be used anywhere in the array literal, and may be used more than once.
const arr = [1, 2, 3]; const arr2 = [...arr]; // like arr.slice() arr2.push(4); // arr2 becomes [1, 2, 3, 4] // arr remains unaffected
注意:
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
Object.assign()
and spread syntax.)
const a = [[1], [2], [3]]; const b = [...a]; b.shift().shift(); // 1 // Oh no! Now array 'a' is affected as well: a // [[], [2], [3]]
Array.prototype.concat()
is often used to concatenate an array to the end of an existing array. Without spread syntax, this is done as:
const arr1 = [0, 1, 2]; const arr2 = [3, 4, 5]; // Append all items from arr2 onto arr1 arr1 = arr1.concat(arr2);
With spread syntax this becomes:
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)
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:
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]
With spread syntax, this becomes:
let arr1 = [0, 1, 2]; let arr2 = [3, 4, 5]; arr1 = [...arr2, ...arr1]; // arr1 is now [3, 4, 5, 0, 1, 2]
注意:
不像
unshift()
, this creates a new
arr1
, and does not modify the original
arr1
array in-place.
Rest/Spread Properties for ECMAScript
proposal (ES2018) added spread properties to
object literals
. It copies own enumerable properties from a provided object onto a new object.
Shallow-cloning (excluding prototype) or merging of objects is now possible using a shorter syntax than
Object.assign()
.
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 }
注意,
Object.assign()
triggers
setters
, whereas spread syntax doesn't.
Note that you cannot replace or mimic the
Object.assign()
函数:
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 } }
In the above example, the spread syntax does not work as one might expect: it spreads an array of arguments into the object literal, due to the rest parameter.
Objects themselves are not iterable, but they become iterable when used in an Array, or with iterating functions such as
map()
,
reduce()
,和
assign()
. When merging 2 objects together with the spread operator, it is assumed another iterating function is used when the merging occurs.
Spread syntax (other than in the case of spread properties) can be applied only to iterable 对象:
const obj = {'key1': 'value1'};
const array = [...obj]; // TypeError: obj is not iterable
When using spread syntax for function calls, be aware of the possibility of exceeding the JavaScript engine's argument length limit. See
apply()
了解更多细节。
| 规范 |
|---|
|
ECMAScript (ECMA-262)
The definition of 'Array initializer' in that specification. |
|
ECMAScript (ECMA-262)
The definition of 'Object initializer' in that specification. |
| Desktop | Mobile | Server | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Spread in array literals | Chrome 46 | Edge 12 | Firefox 16 | IE No | Opera 37 | Safari 8 | WebView Android 46 | Chrome Android 46 | Firefox Android 16 | Opera Android 37 | Safari iOS 8 | Samsung Internet Android 5.0 |
nodejs
5.0.0
|
| Spread in function calls | Chrome 46 | Edge 12 | Firefox 27 | IE No | Opera 37 | Safari 8 | WebView Android 46 | Chrome Android 46 | Firefox Android 27 | Opera Android 37 | Safari iOS 8 | Samsung Internet Android 5.0 |
nodejs
5.0.0
|
| Spread in destructuring | Chrome 49 | Edge 79 | Firefox 34 | IE No | Opera 37 | Safari 10 | WebView Android 49 | Chrome Android 49 | Firefox Android 34 | Opera Android 37 | Safari iOS 10 | Samsung Internet Android 5.0 | nodejs 6.0.0 |
| Spread in object literals | Chrome 60 | Edge 79 | Firefox 55 | IE No | Opera 47 | Safari 11.1 | WebView Android 60 | Chrome Android 60 | Firefox Android 55 | Opera Android 44 | Safari iOS 11.3 | Samsung Internet Android 8.2 |
nodejs
8.3.0
|
完整支持
不支持
实验。期望将来行为有所改变。
用户必须明确启用此特征。
...
’)
Function.prototype.apply()
(also ‘
...
’)