Back to Blog
Tutorial2025-01-04

Spread vs Rest Operators: Same Syntax, Different Purpose

Master the three dots (...) in JavaScript. Learn when they spread and when they collect, with practical examples.

Same Syntax, Opposite Actions

The three dots ... in JavaScript can do two opposite things: - Spread: Expands elements (unpacking) - Rest: Collects elements (packing)

The difference is all about context - where you use them.

Spread Operator: Expanding Things

Use spread when you want to expand or copy elements.

Spreading Arrays

``javascript const fruits = ['apple', 'banana']; const vegetables = ['carrot', 'broccoli'];

// Combine arrays const food = [...fruits, ...vegetables]; // ['apple', 'banana', 'carrot', 'broccoli']

// Copy an array (shallow) const fruitsCopy = [...fruits]; // ['apple', 'banana']

// Add elements while spreading const moreFruits = ['mango', ...fruits, 'orange']; // ['mango', 'apple', 'banana', 'orange'] `

Spreading Objects

`javascript const user = { name: 'Alice', age: 25 }; const location = { city: 'Seoul', country: 'Korea' };

// Combine objects const profile = { ...user, ...location }; // { name: 'Alice', age: 25, city: 'Seoul', country: 'Korea' }

// Copy and override const updatedUser = { ...user, age: 26 }; // { name: 'Alice', age: 26 }

// Later properties win const merged = { ...user, name: 'Bob' }; // { name: 'Bob', age: 25 } `

Spreading in Function Calls

`javascript const numbers = [1, 5, 3, 9, 2];

// Instead of: Math.max(1, 5, 3, 9, 2) Math.max(...numbers); // 9

// Convert array to arguments function greet(name, age, city) { console.log(Hi ${name}, ${age} from ${city}); }

const args = ['Alice', 25, 'Seoul']; greet(...args); // "Hi Alice, 25 from Seoul" `

Rest Operator: Collecting Things

Use rest when you want to collect remaining elements.

Rest in Function Parameters

`javascript // Collect all arguments function sum(...numbers) { return numbers.reduce((a, b) => a + b, 0); }

sum(1, 2, 3); // 6 sum(1, 2, 3, 4, 5); // 15

// Collect remaining arguments function introduce(greeting, ...names) { names.forEach(name => console.log(${greeting}, ${name}!)); }

introduce('Hello', 'Alice', 'Bob', 'Charlie'); // "Hello, Alice!" // "Hello, Bob!" // "Hello, Charlie!" `

Rest in Destructuring

`javascript // Array destructuring const [first, second, ...rest] = [1, 2, 3, 4, 5]; console.log(first); // 1 console.log(second); // 2 console.log(rest); // [3, 4, 5]

// Object destructuring const user = { name: 'Alice', age: 25, city: 'Seoul', country: 'Korea' }; const { name, ...details } = user; console.log(name); // 'Alice' console.log(details); // { age: 25, city: 'Seoul', country: 'Korea' } `

The Key Difference: Context

| Location | What it does | Name | |----------|--------------|------| | Function call: fn(...arr) | Expands | Spread | | Array literal: [...arr] | Expands | Spread | | Object literal: {...obj} | Expands | Spread | | Function param: fn(...args) | Collects | Rest | | Destructuring: [a, ...rest] | Collects | Rest |

Simple rule: - Right side of = or in function calls → Spread (expanding) - Left side of = or in function definition → Rest (collecting)

Practical Patterns

1. Immutable Updates (React/Redux)

`javascript // Add item to array const addTodo = (todos, newTodo) => [...todos, newTodo];

// Remove item from array const removeTodo = (todos, id) => todos.filter(t => t.id !== id);

// Update item in array const updateTodo = (todos, id, updates) => todos.map(t => t.id === id ? { ...t, ...updates } : t);

// Update nested state const state = { user: { name: 'Alice', settings: { theme: 'dark' } } };

const newState = { ...state, user: { ...state.user, settings: { ...state.user.settings, theme: 'light' } } }; `

2. Function Composition

`javascript // Wrapper that logs calls function withLogging(fn) { return function(...args) { console.log('Called with:', args); return fn(...args); }; }

const add = (a, b) => a + b; const loggedAdd = withLogging(add);

loggedAdd(2, 3); // "Called with: [2, 3]" -> 5 `

3. Removing Properties

`javascript const user = { name: 'Alice', password: 'secret', age: 25 };

// Remove sensitive data const { password, ...safeUser } = user; console.log(safeUser); // { name: 'Alice', age: 25 } `

4. Default Values with Spread

`javascript const defaults = { theme: 'light', language: 'en', fontSize: 14 }; const userPrefs = { theme: 'dark' };

const settings = { ...defaults, ...userPrefs }; // { theme: 'dark', language: 'en', fontSize: 14 } `

Common Mistakes

Mistake 1: Shallow Copy Only

`javascript const original = { user: { name: 'Alice' } }; const copy = { ...original };

copy.user.name = 'Bob'; console.log(original.user.name); // 'Bob' - original changed!

// Fix: Deep copy nested objects const deepCopy = { ...original, user: { ...original.user } }; `

Mistake 2: Rest Must Be Last

`javascript // WRONG const [...first, last] = [1, 2, 3]; // SyntaxError

// RIGHT const [first, ...rest] = [1, 2, 3]; `

Mistake 3: Spreading Non-Iterables

`javascript const num = 42; const arr = [...num]; // TypeError: num is not iterable

// Works with strings (they're iterable) const str = 'hello'; const chars = [...str]; // ['h', 'e', 'l', 'l', 'o'] `

Summary

- Spread (...): Expands arrays/objects - use on right side - Rest (...): Collects into arrays/objects - use on left side - Both use ...` syntax but do opposite things - Spread creates shallow copies, not deep - Rest parameter must be last in the list