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