# Spread and Rest Operators in JavaScript: One Syntax, Two Superpowers


> Three dots. That's all it is — `...` — yet depending on where you place them,
> they do completely opposite things. This guide breaks both apart with clear
> examples and real-world patterns.

---

## 1. The Core Idea: Expanding vs. Collecting

The `...` syntax has two personalities:

```mermaid
flowchart LR
    S["**...spread**\nexpands one thing\ninto many"]:::teal
    R["**...rest**\ncollects many things\ninto one"]:::coral

    S -. "opposite directions" .- R

    classDef teal  fill:#E1F5EE,stroke:#0F6E56,color:#085041
    classDef coral fill:#FAECE7,stroke:#993C1D,color:#712B13
```

| | Spread `...` | Rest `...` |
|---|---|---|
| Direction | Expands outward | Collects inward |
| Used in | Function calls, array/object literals | Function parameters, destructuring |
| Think of it as | Unpacking a suitcase | Packing a suitcase |

---

## 2. The Spread Operator — Expanding Values

Spread **unpacks** an iterable (array, string, object) and spreads its elements
into a new context.

### Visualised

```mermaid
flowchart LR
    A["[1, 2, 3]"]:::box -->|"...spread"| B["1"]:::item
    A -->|"...spread"| C["2"]:::item
    A -->|"...spread"| D["3"]:::item
    B & C & D --> E["[1, 2, 3, 4, 5]"]:::result

    classDef box    fill:#E1F5EE,stroke:#0F6E56,color:#085041
    classDef item   fill:#EEEDFE,stroke:#534AB7,color:#3C3489
    classDef result fill:#EAF3DE,stroke:#3B6D11,color:#27500A
```

### Spread with arrays

```js
const a = [1, 2, 3];
const b = [4, 5];

// Merge two arrays
const merged = [...a, ...b];
console.log(merged); // [1, 2, 3, 4, 5]

// Insert values at a specific position
const withMiddle = [0, ...a, 99];
console.log(withMiddle); // [0, 1, 2, 3, 99]

// Copy an array (no shared reference)
const copy = [...a];
copy.push(100);
console.log(a);    // [1, 2, 3]  ← original untouched
console.log(copy); // [1, 2, 3, 100]
```

### Spread in function calls

```js
const nums = [5, 10, 3, 8];

// Without spread — wrong: passes the whole array as one argument
Math.max(nums);    // NaN

// With spread — correct: passes each element as a separate argument
Math.max(...nums); // 10
```

### Spread with objects

```js
const defaults = { theme: "light", language: "en", fontSize: 14 };
const userPrefs = { theme: "dark", fontSize: 16 };

// Merge objects — later keys overwrite earlier ones
const settings = { ...defaults, ...userPrefs };
console.log(settings);
// { theme: "dark", language: "en", fontSize: 16 }
```

---

## 3. The Rest Operator — Collecting Values

Rest does the opposite: it **gathers** multiple values into a single array.
You'll see it in two places — function parameters and destructuring.

### Visualised

```mermaid
flowchart LR
    B["10"]:::item --> E["args\n[10, 20, 30, 40]"]:::result
    C["20"]:::item --> E
    D["30"]:::item --> E
    F["40"]:::item --> E

    classDef item   fill:#EEEDFE,stroke:#534AB7,color:#3C3489
    classDef result fill:#FAECE7,stroke:#993C1D,color:#712B13
```

### Rest in function parameters

```js
// Collect all arguments into a single array
function sum(...numbers) {
  return numbers.reduce((total, n) => total + n, 0);
}

console.log(sum(1, 2, 3));        // 6
console.log(sum(10, 20, 30, 40)); // 100
```

You can combine a named first parameter with rest:

```js
function greet(firstName, ...titles) {
  console.log(`Hello, ${titles.join(" ")} ${firstName}!`);
}

greet("Alice", "Dr.", "Prof."); // Hello, Dr. Prof. Alice!
```

> **Rule:** Rest must always be the **last** parameter. `(a, ...b, c)` is a
> syntax error.

### Rest in array destructuring

```js
const scores = [98, 85, 76, 60, 55];

const [first, second, ...theRest] = scores;

console.log(first);   // 98
console.log(second);  // 85
console.log(theRest); // [76, 60, 55]
```

### Rest in object destructuring

```js
const user = { name: "Bob", age: 25, city: "Mumbai", role: "admin" };

const { name, role, ...details } = user;

console.log(name);    // "Bob"
console.log(role);    // "admin"
console.log(details); // { age: 25, city: "Mumbai" }
```

---

## 4. Spread vs. Rest — Side by Side

```js
// ---- SPREAD ----
// Takes an array, expands it into individual arguments
const nums = [3, 1, 4, 1, 5];
console.log(Math.max(...nums)); // 5
//                  ↑ spread

// ---- REST ----
// Takes individual arguments, collects them into an array
function add(...nums) {
  return nums.reduce((a, b) => a + b, 0);
}
//             ↑ rest
console.log(add(3, 1, 4, 1, 5)); // 14
```

Same three dots. Same variable name. Completely different jobs.

---

## 5. Practical Use Cases

### Clone without reference (arrays)

```js
const original = [1, 2, 3];
const clone    = [...original];   // safe copy

clone.push(99);
console.log(original); // [1, 2, 3]  ← untouched
```

### Merge and override object settings

```js
const baseConfig = { debug: false, timeout: 3000, retries: 3 };
const devConfig  = { debug: true };

const config = { ...baseConfig, ...devConfig };
// { debug: true, timeout: 3000, retries: 3 }
```

### Remove a key from an object (without mutating)

```js
const user = { id: 1, name: "Alice", password: "s3cr3t" };

const { password, ...safeUser } = user;   // rest in destructuring
console.log(safeUser); // { id: 1, name: "Alice" }
// password is gone — safe to send to the client
```

### Convert a string into characters

```js
const word = "hello";
const chars = [...word];
console.log(chars); // ["h", "e", "l", "l", "o"]
```

### Pass dynamic arguments to a function

```js
function createTag(tag, ...classes) {
  return `<${tag} class="${classes.join(" ")}">`;
}

createTag("div", "card", "shadow", "rounded");
// "<div class="card shadow rounded">"
```

### Combine arrays from multiple sources

```js
const frontend = ["React", "CSS", "HTML"];
const backend  = ["Node", "Express"];
const database = ["MongoDB"];

const fullStack = [...frontend, ...backend, ...database];
// ["React", "CSS", "HTML", "Node", "Express", "MongoDB"]
```

---

## 6. Common Mistakes to Avoid

**Spreading a non-iterable**

```js
const num = 42;
const bad = [...num]; // TypeError: num is not iterable
```

**Putting rest before the last position**

```js
function wrong(a, ...b, c) {} // SyntaxError: rest must be last
function right(a, b, ...c) {} // ✅
```

**Expecting deep cloning from spread**

```js
const nested = { a: { b: 1 } };
const copy   = { ...nested };

copy.a.b = 99;
console.log(nested.a.b); // 99 ← original changed!
// spread is shallow — nested objects are still shared references
```

For deep cloning, use `structuredClone(obj)` instead.

---

## Key Takeaways

- `...spread` **expands** an array or object into individual elements
- `...rest` **collects** individual elements into a single array
- Spread lives in **expressions** (function calls, literals)
- Rest lives in **definitions** (function parameters, destructuring)
- Spread is great for merging, copying, and passing arguments
- Rest is great for variadic functions and picking properties apart
- Both are shallow — nested objects share the same reference

---

*Enjoyed this? Follow for the next article in the series: destructuring in depth —
arrays, objects, defaults, and aliases. 👇*
