The spread operator (...) copies all enumeralbe properties from a source object into a target object. For flat objects, this creates a new object with copied primitive values.
const source = { x: 10, y: 20 };
const target = { ...source }; // { x: 10, y: 20 }
This is equivalent to using Object.assign() with an empty target:
const target = Object.assign({}, source);
Object.assign() merges sources into a target, where later properties override earlier ones. Similarly, properties after the spread operator override those from it.
const updated = { ...source, ...{ x: 30 } }; // { x: 30, y: 20 }
This patttern is useful for immutable updates, such as in Redux reducers, where state must not be mutated.
However, the spread operator performs a shallow copy. Primitive values are duplicated, but nested objects are copied by reference.
const original = { id: 1, nested: { value: 'test' } };
const copy = { ...original };
copy.nested.value = 'changed';
console.log(original.nested.value); // 'changed'
Here, modifying copy.nested affects original.nested because both reference the same object.
For arrays, the spread operator can convert an array to a list of arguments:
function sum(a, b) {
return a + b;
}
const numbers = [5, 7];
sum(...numbers); // 12
It also copies arrays, avoiding reference sharing:
const arr1 = [1, 2];
const arr2 = [...arr1]; // Independent copy
arr2[0] = 9;
console.log(arr1); // [1, 2]
When combined witth destructuring, it gathers remaining elements:
const [head, ...tail] = [1, 2, 3];
console.log(tail); // [2, 3]
The spread element must be last in destructuring; placing it elsewhere causes an error.
It converts iterables like strings to arrays:
[...'code']; // ['c', 'o', 'd', 'e']
Any iterable, such as arguments, can be transformed:
function convertArgs() {
return [...arguments];
}
This replaces older methods like Array.prototype.slice.call(arguments).