JavaScript's ... operator does two opposite things depending on where you use it. Spread expands a collection into individual pieces. RestWhat is rest?An architectural style for web APIs where URLs represent resources (nouns) and HTTP methods (GET, POST, PUT, DELETE) represent actions on those resources. gathers individual pieces into a collection. Same syntax, opposite directions. Once you internalize this, you will reach for ... constantly, it is one of the most versatile features in modern JavaScript.
The spread operatorWhat is spread operator?The three-dot syntax (...) that expands an array or object into its individual elements, commonly used to copy or merge collections.: expanding values
Spread takes an iterable (array, string, object) and expands it into individual elements. Think of it as unpacking a suitcase.
Spreading arrays
const numbers = [1, 2, 3];
const moreNumbers = [...numbers, 4, 5];
// [1, 2, 3, 4, 5]
const combined = [0, ...numbers, 4];
// [0, 1, 2, 3, 4]Copying arrays without modifying the original:
const original = ['Alice', 'Bob', 'Charlie'];
const copy = [...original];
copy.push('David');
console.log(original); // ['Alice', 'Bob', 'Charlie'] - unchanged
console.log(copy); // ['Alice', 'Bob', 'Charlie', 'David']const copy = { ...bigObject } and you mutate a nested property on copy, the original changes too. For deep data, use structuredClone() instead.Spreading objects
const user = { name: 'Alice', age: 25 };
// Copy and extend
const withEmail = { ...user, email: 'alice@example.com' };
// { name: 'Alice', age: 25, email: 'alice@example.com' }
// Override existing properties - last one wins
const olderUser = { ...user, age: 26 };
// { name: 'Alice', age: 26 }Merging objects with overrides is the most common pattern:
const defaults = { theme: 'light', fontSize: 14, showSidebar: true };
const userPrefs = { theme: 'dark', fontSize: 16 };
const settings = { ...defaults, ...userPrefs };
// { theme: 'dark', fontSize: 16, showSidebar: true }Shallow copyWhat is shallow copy?A copy of an object where top-level values are duplicated but nested objects still point to the same originals. vs deep copy
This is the single most misunderstood aspect of spread. Spread only copies one level deep.
| Source data | Spread result | Safe to mutate copy? |
|---|---|---|
Flat object { a: 1, b: 2 } | Independent copy | Yes |
Nested object { a: { x: 1 } } | a is shared reference | No, mutating copy.a.x changes original |
Array of primitives [1, 2, 3] | Independent copy | Yes |
Array of objects [{ id: 1 }] | Objects are shared | No, mutating copy[0].id changes original |
// Shallow copy problem
const user = { name: 'Alice', prefs: { theme: 'dark' } };
const clone = { ...user };
clone.prefs.theme = 'light';
console.log(user.prefs.theme); // 'light' - original was mutated!
// Deep copy solution
const safeClone = structuredClone(user);Spreading in function calls
const numbers = [1, 5, 3, 9, 2];
const min = Math.min(...numbers); // 1
const max = Math.max(...numbers); // 9The restWhat is rest?An architectural style for web APIs where URLs represent resources (nouns) and HTTP methods (GET, POST, PUT, DELETE) represent actions on those resources. operator: collecting values
While spread expands, rest collects. It gathers remaining elements into an array or object.
Rest in function parameters
function sum(...numbers) {
return numbers.reduce((total, n) => total + n, 0);
}
sum(1, 2, 3, 4); // 10
sum(10, 20); // 30
sum(); // 0The rest parameter must be last and can combine with regular parameters:
function logMessage(level, ...details) {
console.log(`[${level}]`, ...details);
}
logMessage('ERROR', 'File not found', '/data/file.txt');
// [ERROR] File not found /data/file.txtRest in destructuringWhat is destructuring?A shorthand for pulling specific values out of an object or array into their own variables instead of accessing them one by one.
Extract specific properties while keeping everything else:
const user = {
id: 42,
name: 'Alice',
email: 'alice@example.com',
role: 'admin',
password: 'secret'
};
const { password, ...safeUser } = user;
// safeUser = { id: 42, name: 'Alice', email: '...', role: 'admin' }
// password is extracted and discardedThis pattern is common for stripping sensitive fields before sending data to clients.
Rest with arrays
const colors = ['red', 'green', 'blue', 'yellow', 'purple'];
const [primary, secondary, ...others] = colors;
console.log(primary); // 'red'
console.log(secondary); // 'green'
console.log(others); // ['blue', 'yellow', 'purple']Spread vs restWhat is rest?An architectural style for web APIs where URLs represent resources (nouns) and HTTP methods (GET, POST, PUT, DELETE) represent actions on those resources. at a glance
| Context | Operator | Direction | Example |
|---|---|---|---|
| Function call | Spread | Expand array into arguments | myFunc(...arr) |
| Array literal | Spread | Expand into elements | [...arr, 4] |
| Object literal | Spread | Expand into properties | { ...obj } |
| Function param | Rest | Collect arguments | function(...args) |
| Destructuring | Rest | Collect remaining | const [a, ...rest] = arr |
Memory aid: Spread = "take these apart" (outward). Rest = "gather the rest" (inward).
Common patterns
Immutable updates (React-style)
const todos = [
{ id: 1, text: 'Learn spread', done: false },
{ id: 2, text: 'Learn rest', done: false }
];
// Add a new todo without mutating
const withNew = [...todos, { id: 3, text: 'Practice', done: false }];
// Toggle a todo without mutating
const toggled = todos.map(t =>
t.id === 1 ? { ...t, done: !t.done } : t
);Removing duplicates
const numbers = [1, 2, 3, 3, 4, 4, 5];
const unique = [...new Set(numbers)];
// [1, 2, 3, 4, 5]Flexible function arguments
function createUser(name, age, ...tags) {
return { name, age, tags, createdAt: new Date() };
}
createUser('Alice', 25);
// { name: 'Alice', age: 25, tags: [], createdAt: ... }
createUser('Bob', 30, 'developer', 'javascript');
// { name: 'Bob', age: 30, tags: ['developer', 'javascript'], ... }Quick reference
| Pattern | Syntax | Result |
|---|---|---|
| Copy array | [...arr] | Shallow copy |
| Merge arrays | [...a, ...b] | Combined array |
| Copy object | { ...obj } | Shallow copy |
| Merge objects | { ...a, ...b } | b overrides a |
| Collect args | function(...args) | args is a real array |
| Destructure rest | const { a, ...rest } = obj | rest has everything except a |
| Deep copy | structuredClone(obj) | True deep copy (not spread) |