Rest & Spread
The ... token does two different (but related) jobs in JavaScript: it gathers values into one variable (rest), or it spreads values out of one (spread). The syntax looks identical; what it does depends on where you write it.
The three contexts
Rest parameter — in a function signature:
function f(...args)collects extra arguments into an array.Spread in a call — calling a function with
f(...arr)passes each element ofarras a separate argument.Spread in a literal —
[...arr]or{...obj}copies the elements/properties into a new array or object.
If the ... is on the left of = or in a parameter list, it is rest. If it is on the right, inside an array/object literal, or in a function call, it is spread.
Rest parameter — variable arguments
function sum(...nums) {
return nums.reduce((acc, n) => acc + n, 0);
}
console.log(sum()); // 0
console.log(sum(1, 2, 3)); // 6
console.log(sum(1, 2, 3, 4, 5)); // 15Unlike the old arguments object, nums is a real array — map, filter, reduce all work directly on it. It also works inside arrow functions, where arguments does not.
function log(level, ...messages) {
console.log("[" + level + "]", ...messages);
}
log("info", "user", "ada", "logged in");Spread in function calls
Use ... to pass an array as separate arguments to a function.
const nums = [3, 9, 1, 7, 5];
console.log(Math.max(nums)); // NaN — passing the whole array
console.log(Math.max(...nums)); // 9 — spreads to Math.max(3, 9, 1, 7, 5)
function greet(a, b, c) {
return `${a}, ${b}, ${c}`;
}
const parts = ["Hello", "Ada", "!"];
console.log(greet(...parts)); // Hello, Ada, !Spread in arrays
Spread lets you copy, concat, and merge arrays in a single expression.
const a = [1, 2, 3]; const b = [4, 5, 6]; const copy = [...a]; // [1, 2, 3] — shallow copy const merged = [...a, ...b]; // [1, 2, 3, 4, 5, 6] const middle = [0, ...a, 99, ...b]; // mix and match anywhere console.log(copy, merged, middle);
This is now the idiomatic way to clone or extend arrays — clearer than Array.from, concat, or slice.
Spread in objects
Object spread (ES2018) copies a shallow set of properties from one object to another.
const user = { name: "Ada", role: "admin" };
const copy = { ...user };
const update = { ...user, role: "user" }; // override role
const audit = { ...user, lastSeen: Date.now() }; // add property
console.log(update);
console.log(audit);{ name: 'Ada', role: 'user' }
{ name: 'Ada', role: 'admin', lastSeen: ... }Object spread is the foundation of immutable updates — popular in Redux reducers, React state, and any functional-style code.
Rest in destructuring
On the receiving side, ... collects "the rest" of an array or object into a new variable.
const [first, second, ...others] = [1, 2, 3, 4, 5];
console.log(first, second, others); // 1 2 [ 3, 4, 5 ]
const { id, ...rest } = { id: 1, name: "Ada", role: "admin" };
console.log(id); // 1
console.log(rest); // { name: 'Ada', role: 'admin' }Shallow, not deep
Both array and object spread are shallow: nested objects are still shared by reference.
const original = { name: "Ada", address: { city: "London" } };
const copy = { ...original };
copy.address.city = "Paris";
console.log(original.address.city); // "Paris" — same nested object!