All topics
ES6+beginner

Spread and Rest Operators

The three-dot syntax that expands an iterable into individual elements (spread) or collects multiple elements into a single array (rest), depending on context.

The same three dots do opposite things depending on where they appear.

Spread is like pouring an entire box's contents into a bigger box you're filling. Rest is the reverse: sweeping up whatever's left after everyone's grabbed their labeled item into one catch-all box.

Key Concepts

1
    spread   expands an iterable into individual items
    rest     collects individual items into an array
2
    const copy = [...arr];               // spread
    function f(...args) { }              // rest
    const [first, ...others] = arr;      // rest
    f(...arr);                           // spread
3
The rule: rest gathers on the left of an assignment or in a parameter list. Spread scatters everywhere else.Arrays.
4
    [...a, ...b]              concatenate
    [...new Set(a)]           deduplicate
    [...a].sort()             sort a copy, not the original
    [...'hello']             ['h','e','l','l','o']
    Math.max(...numbers)      pass an array as arguments
5
Objects (ES2018).
6
    { ...defaults, ...options }        later wins
    { ...obj, id: 5 }                  override one field
    const { id, ...rest } = obj;       omit one field
7
That last pattern — destructuring with rest — is the standard way to remove a property without mutating.
8
Both are shallow.
    const copy = { ...orig };
    copy.nested === orig.nested;       // true -- still shared
9
Which is the most common misunderstanding: spread copies the top level only.
10
Object spread only copies own enumerable properties. Getters are evaluated and become plain values; prototype methods are not copied at all. Spreading a class instance gives you a plain object with its data and none of its behaviour.
11
Spread works on any iterable; object spread works on anything.
    [...{ a: 1 }]          TypeError -- not iterable
    { ...[1, 2] }          { 0: 1, 1: 2 } -- indices as keys
    { ...'ab' }            { 0: 'a', 1: 'b' }
12
Rest must be last.
    function f(...args, last) { }      SyntaxError
    const [...rest, last] = arr;       SyntaxError
13
One performance note. Spreading in a loop is quadratic:
14
    // O(n²) -- a new array each iteration
    items.reduce((acc, x) => [...acc, x], []);
15
    // O(n)
    items.reduce((acc, x) => { acc.push(x); return acc; }, []);
16
Fine for small arrays, and a real problem at a few thousand items.
17
What the interviewer is probing.1. "Same syntax — what distinguishes them?" Probing: position. Stalls: "They are the same." Moves up: rest gathers on the left of an assignment or in a parameter list; spread expands on the right or in a call.
18
2. "How deep does spread copy?" Probing: the depth. Stalls: "Fully." Moves up: one level — nested objects remain shared references.
19
3. "What happens spreading a string?" Probing: iterables. Stalls: "An error." Moves up: it becomes an array of characters, because strings are iterable — and it handles surrogate pairs better than split("").
20
4. "What order wins when spreading two objects?" Probing: the merge rule. Stalls: "The first." Moves up: later properties overwrite earlier ones, which is how defaults-then-overrides is written.