JavaScriptRest & Spread

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 of arr as 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

JS
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)); // 15

Unlike 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.

Rest must come last
Only the *final* parameter can be a rest parameter — it collects "everything else after the named ones".

JS
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.

JS
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.

JS
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.

JS
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.

JS
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.

JS
const original = { name: "Ada", address: { city: "London" } };
const copy = { ...original };

copy.address.city = "Paris";
console.log(original.address.city);   // "Paris" — same nested object!
Deep cloning
For true deep copies, use `structuredClone(value)` (built into modern engines) or a library. Spread is great for one-level cases, which covers most state-update needs.