Spread vs Rest Operators in JavaScript
Expand what you have, collect what you need — mastering spread and rest operators in JavaScript
Search for a command to run...
Expand what you have, collect what you need — mastering spread and rest operators in JavaScript
No comments yet. Be the first to comment.
Node.js doesn't use multiple threads to handle concurrency — and that's not a weakness. Here's exactly how it pulls off handling thousands of connections without breaking a sweat.

Stop confusing params and query in Express.js

Table of Contents What is cURL — in very simple terms Why programmers need cURL Making your first request Understanding request and response Using cURL to talk to APIs Common mistakes beginners

A practical guide to where files live, how to expose them over HTTP, and the security rules you cannot skip.

Understanding authentication approaches · stateful vs stateless · when to use each

XP-VISHAL-WEB-DEV-COHORT-2026-BLOGS
24 posts
Both operators are written as three dots — ... — yet they do completely opposite things depending on where you place them. This single syntactic choice led to one of the most common points of confusion in modern JavaScript. The good news: once you understand the core mental model, it clicks permanently.
Think of it like breathing: spread is exhaling — pushing values outward into a space — while rest is inhaling — pulling values inward into a container. The dots are the same; the direction of data is opposite.
Chapter 01
The spread operator takes an iterable — an array, string, Set, or any object with [Symbol.iterator] — and expands its contents in-place. You use it wherever a list of values is expected.
const fruits = ['apple', 'banana'];
const veggies = ['carrot', 'daikon'];
// Combine arrays — no .concat() needed
const grocery = [...fruits, ...veggies];
// ['apple', 'banana', 'carrot', 'daikon']
// Clone an array (shallow copy)
const copy = [...fruits];
// Insert in the middle
const mixed = ['lemon', ...fruits, 'fig'];
// ['lemon', 'apple', 'banana', 'fig']
Before spread, passing an array to a function expecting individual arguments required .apply(). Now it's effortless:
const scores = [42, 91, 17, 68];
// Old way
Math.max.apply(null, scores);
// New way — clean and readable
Math.max(...scores); // 91
// Works with any function
function greet(name, role) {
return `Hello \({name}, the \){role}!`;
}
const args = ['Ada', 'engineer'];
greet(...args); // 'Hello Ada, the engineer!'
Object spread (ES2018) copies enumerable own properties from one object into another — the cleaner alternative to Object.assign():
const defaults = { theme: 'light', lang: 'en', grid: 12 };
const userPrefs = { theme: 'dark', lang: 'fr' };
// Merge with override — later keys win
const config = { ...defaults, ...userPrefs };
// { theme: 'dark', lang: 'fr', grid: 12 }
// Shallow clone an object
const snapshot = { ...config };
// Add / override specific properties
const updated = { ...config, grid: 16 };
// { theme: 'dark', lang: 'fr', grid: 16 }
⚠️ Shallow Only
Spread creates a shallow copy. Nested objects and arrays are still shared by reference. If you need a deep clone, use
structuredClone(obj)or a library like Lodash.
Chapter 02
Rest does the inverse. It collects multiple individual values into a single array. You see it in two places: function parameter lists and destructuring assignments.
When you don't know how many arguments a function will receive, rest gathers the extras into a real array — replacing the old, awkward arguments object:
// Sum any number of arguments
function sum(...nums) {
return nums.reduce((acc, n) => acc + n, 0);
}
sum(1, 2, 3); // 6
sum(10, 20, 30, 40); // 100
// Mix named params with rest
function logger(level, ...messages) {
messages.forEach(msg =>
console.log(`[\({level}] \){msg}`)
);
}
logger('INFO', 'Server started', 'Port 3000', 'PID 1042');
📌 Rest Must Be Last
The rest parameter must always be the final parameter in the list. Writing
function bad(...items, last)is a syntax error — rest collects everything remaining, so nothing can follow it.
const [first, second, ...remaining] = [10, 20, 30, 40, 50];
console.log(first); // 10
console.log(second); // 20
console.log(remaining); // [30, 40, 50]
// Useful for grabbing a queue's head
const [head, ...tail] = taskQueue;
const user = {
id: 7,
name: 'Alan',
password: 's3cr3t',
role: 'admin'
};
// Pluck what you need; collect the rest
const { password, ...safeUser } = user;
console.log(safeUser);
// { id: 7, name: 'Alan', role: 'admin' }
// safeUser is now safe to expose publicly
Chapter 03
| Property | Spread ... | Rest ... |
|---|---|---|
| Direction | Expands outward | Collects inward |
| Used in | Function calls, array literals, object literals | Function parameters, destructuring |
| Input type | Iterable or object | Multiple individual values |
| Output type | Individual values | An Array |
| Position rule | Anywhere in the list | Must be last |
| Count | Use multiple times | Only once per signature |
Chapter 04
Here's a realistic example that uses both operators in a single function — a common pattern in component-based UI code:
// REST: collect all extra props not explicitly named
function Button({ label, variant = 'primary', ...rest }) {
const baseStyles = ['btn', `btn-${variant}`];
// SPREAD: expand baseStyles into a new array
const classes = [...baseStyles, rest.className]
.filter(Boolean)
.join(' ');
// SPREAD: forward all remaining props to the element
return `<button class="\({classes}">\){label}</button>`;
}
const extraProps = { disabled: true, id: 'submit' };
// SPREAD: unpack extraProps into named arguments
Button({ label: 'Save', variant: 'danger', ...extraProps });
Chapter 05
Spreading a plain object into an array literal will throw a TypeError because plain objects don't implement the iterable protocol. Object spread only works inside {}.
const obj = { a: 1 };
[...obj] // ❌ TypeError: obj is not iterable
({ ...obj }) // ✅ { a: 1 } — object spread works fine
// Strings ARE iterable
[...'hello'] // ✅ ['h','e','l','l','o']
Rest Doesn't Work Twice
You can only have one rest element per destructuring pattern, and it must be last. Multiple rests in the same assignment is a syntax error.
const [...a, last] = [1,2,3]; // ❌ Rest must be last
const [...a, ...b] = [1,2,3]; // ❌ Only one rest allowed
const [x, y, ...rest] = [1,2,3]; // ✅ Correct
Takeaway
Position determines purpose. When ... appears where values are consumed (function arguments, array literals, object literals), it is Spread. When ... appears where values are defined or captured (function parameters, destructuring patterns), it is Rest.
Master this distinction and you'll read and write modern JavaScript with far more confidence — and far fewer head-scratching moments at 2 AM.