The spread and rest operators
The three dots ... do two opposite things in JavaScript: they spread an array into a list of values, or gather several values into an array. The first is called spread, the second rest.
Spreading an array
Placed before an array, ... inserts its elements one by one, as if you had listed them separated by commas:
console.log(...[1, 2, 3]); // 1 2 3This is handy when a function expects separate values but you have an array:
const numbers = [4, 9, 2];
console.log(Math.max(...numbers)); // 9Copying and merging arrays
Spreading an array inside new square brackets gives you a copy. Several arrays are merged the same way:
const a = [1, 2];
const b = [3, 4];
const merged = [...a, ...b];
console.log(merged.join(', ')); // 1, 2, 3, 4The source arrays are not modified.
Objects
The operator works the same way with objects. Properties written later replace earlier ones with the same name:
const user = { name: 'Anna', age: 25 };
const updated = { ...user, age: 26 };
console.log(updated.name, updated.age); // Anna 26This is the main way to "change" an object without touching the original.
Rest parameters
In a function declaration the three dots gather all remaining arguments into an array:
function sum(...numbers) {
let total = 0;
for (const number of numbers) {
total += number;
}
return total;
}
console.log(sum(1, 2, 3)); // 6There can be only one such parameter, and it must come last.
Rest in destructuring
The same technique works in destructuring: the first variable takes the first element and the rest are gathered into an array:
const [first, ...others] = [1, 2, 3];
console.log(first); // 1
console.log(others.join(', ')); // 2, 3Common mistake
The copy is shallow: nested objects and arrays are not copied but shared.
const original = { tags: ['a'] };
const copy = { ...original };
copy.tags.push('b');
console.log(original.tags.join(', ')); // a, bTo copy data all the way down, use structuredClone(original).