ESC

Type to search the knowledge base.

Rest and Spread Syntax

Collect args with rest, expand iterables with spread — shallow copy pitfalls, parameter order, and object merge patterns.

beginner3 min read
  • javascript
  • rest-and

... does two jobs depending on position: spread expands iterables/objects into places expecting elements/properties; rest collects remaining elements/properties into one binding.

Function rest parameters

function sum(...nums) {
  return nums.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3); // 6

function greet(first, ...rest) {
  // rest is always a real Array
  return [first, ...rest].join(' ');
}

Rest must be last in the parameter list. Prefer rest over legacy arguments (which isn’t a real array and behaves oddly with arrows).

Spread into calls and arrays

const a = [1, 2];
const b = [0, ...a, 3]; // [0,1,2,3]

Math.max(...a); // 2

// clone (shallow)
const copy = [...a];

Spread uses the iterable protocol — works on strings, Maps (keys via default iterator… careful), Sets, NodeLists after conversion patterns, etc.

[...'ab']; // ['a','b']
[...new Set([1, 1, 2])]; // [1,2]

Object spread / rest

const user = { id: 1, name: 'Ada', role: 'admin' };
const { role, ...publicUser } = user;
// publicUser { id, name }

const settings = { theme: 'dark', ...userDefaults, ...overrides };
// later keys win

Object spread is shallow: nested objects are still shared references.

const state = { nested: { n: 1 } };
const next = { ...state, nested: { ...state.nested, n: 2 } };

Common UI patterns

// immutable array update
function updateAt(arr, i, val) {
  return [...arr.slice(0, i), val, ...arr.slice(i + 1)];
}

// props forwarding
function Button({ className, ...rest }) {
  return <button className={cx('btn', className)} {...rest} />;
}

Footguns

Issue Detail
Shallow copy Nested mutability leaks
Huge spreads Math.max(...giantArray) can hit arg limits
Null spread {...null} / {...undefined} OK (no-op); array spread needs iterable
Order Rest params/properties last
// TypeError
// [...null];

Interview answer (out loud)

“Rest gathers remaining args or properties into an array/object; spread expands iterables or own enumerable props into literals or calls. Object and array spreads are shallow copies. Rest parameters replace most arguments use cases and must be last.”

Spread and prototype

const proto = { hidden: true };
const obj = Object.create(proto);
obj.own = 1;
const copy = { ...obj };
copy.own;    // 1
copy.hidden; // undefined — spread only own enumerable props

Same rule as Object.assign. Inherited methods won’t copy via spread.

Arrays: sparse holes

const sparse = [1, , 3];
[...sparse]; // [1, undefined, 3] — holes become undefined

Know this when cloning sparse structures from new Array(n).

Function apply replacement

// old
fn.apply(null, args);
// new
fn(...args);

Both hit argument length limits on enormous arrays; for huge numeric data prefer typed arrays and loops, not spread into calls.

Further reading

Related guides