Used to expand iterable object (e.g. array) to series of arguments.
Example - without spread operator:
const sum = (x, y, z) => x + y + z;
const numbers = [1, 2, 3];
const result = sum(numbers[0], numbers[1], numbers[2]);
With spread operator:
const sum = (x, y, z) => x + y + z;
const numbers = [1, 2, 3];
const result = sum(...numbers);
Notice that the whole array (iterable object = [1, 2, 3]
) is converted into a set of function arguments: 1, 2, 3
.
const numbers = [1, 2, 3];
result = sum(...numbers);
const numbers = [1, 2, 3];
const arr = [...numbers];
Equivalent to:
const arr = [1, 2, 3];
const obj = {
a: "a",
b: 123
};
const cloned = { ...obj };
It creates a (shallow) copy of the object (defined in ES2018).
Example:
const arr3456 = [3, 4, 5, 6];
const arr9 = [1, 2, ...arr3456, 7, 8, 9];
console.log(arr9); // prints: [1, 2, 3, 4, 5, 6, 7, 8, 9];
Ordinary JavaScript behavior:
const obj = {
name: "Bond",
number: 7,
name: "James Bond", // name field specified again
}
console.log(obj.name); // prints: James Bond
When used with spread operator:
const oldObj = {
name: "Bond",
number: 7,
};
const newObj = {
...oldObj,
name: "James Bond",
};
console.log(newObj.name); // prints: James Bond
It is executed from left to right.
This doesn't change anything after all:
const orig = {
character: "Dilbert",
};
const copy = {
character: "Pointy Haired Boss",
...orig
};
console.log(copy); // prints: { character: "Dilbert" }
You can even use spread operator to remove some fields from the original object (used together with destructuring):
const name = {
first: "Arthur",
middle: "Conan",
last: "Doyle",
};
const { middle, ...nameWithoutMiddle } = name;
console.log(nameWithoutMiddle); // prints: { first: "Arthur", last: "Doyle" }
Two variables are "extracted" from the name
variable. The variable nameWithoutMiddle
contains all fields from the original variable with the exception of already extracted fields, i.e. the middle
variable.
Spread operator only copies "first-level" objects, it doesn't deep copy the whole structure.
Example:
const orig = {
num: 123,
arr: [7, 8],
};
const clone = { ...orig };
orig.num = 444;
orig.arr[0] = 99;
console.log(orig); // { num: 444, arr: [ 99, 8 ] }
console.log(clone); // { num: 123, arr: [ 99, 8 ]}
As you can see, clone.num
remains 123 even after it is changed in orig
. However, when orig.arr[0]
is changed, the clone.arr[0]
is changed as well. It is because both arr
references point to the same block of memory, i.e. shallow copy has been used.
If you want to deep-copy this object, you can do it e.g. like this:
const orig = {
num: 123,
arr: [7, 8],
};
const clone = { ...orig, arr: [...orig.arr] };
orig.num = 444;
orig.arr[0] = 99;
console.log(orig); // { num: 444, arr: [ 99, 8 ] }
console.log(clone); // { num: 123, arr: [ 7, 8 ]}
This way, first the whole orig
object is cloned and then the arr
item is cloned independently. Note the number 7
in the output of the last console.log.
The disadvantage of this approach is that you need to know (or detect) structure and types inside the object.
Another possible solution is to use an external library, e.g. lodash method cloneDeep
:-)