16 minLesson 10 of 35
Modern ES6+ JavaScript
Spread & Rest Operators
Spread & Rest Operators
The ... syntax does two opposite things depending on context: spread expands an iterable into individual elements, and rest collects multiple elements into an array. Both are fundamental to modern JavaScript patterns.
Spread with Arrays
// Expand an array into elements
const nums = [1, 2, 3];
console.log(...nums); // 1 2 3 (three separate arguments)
// Copy an array (shallow copy)
const original = [1, 2, 3];
const copy = [...original];
copy.push(4);
console.log(original); // [1, 2, 3] — unmodified
console.log(copy); // [1, 2, 3, 4]
// Combine arrays
const a = [1, 2, 3];
const b = [4, 5, 6];
const combined = [...a, ...b]; // [1, 2, 3, 4, 5, 6]
// Add elements at any position
const withPrefix = [0, ...a]; // [0, 1, 2, 3]
const withSuffix = [...a, 7, 8]; // [1, 2, 3, 7, 8]
const inserted = [...a.slice(0,2), 99, ...a.slice(2)]; // [1, 2, 99, 3]
// Spread in function calls
Math.max(...nums); // 3 — same as Math.max(1, 2, 3)
Math.min(...nums); // 1
Spread with Objects
// Copy an object (shallow)
const user = { name: "Alice", age: 30 };
const copy = { ...user };
// Merge objects
const defaults = { theme: "light", language: "en", fontSize: 16 };
const userPrefs = { theme: "dark", language: "fr" };
const settings = { ...defaults, ...userPrefs };
// { theme: "dark", language: "fr", fontSize: 16 }
// Later properties override earlier ones
// Add/override properties immutably
const updated = { ...user, age: 31, city: "NYC" };
// { name: "Alice", age: 31, city: "NYC" } — user unchanged
// Practical: update one field in an object
function updateUser(user, field, value) {
return { ...user, [field]: value };
}
const newUser = updateUser(user, "name", "Bob");
// { name: "Bob", age: 30 }
Spread in React
// Pass all props to a child component
function Button({ children, ...props }) {
return <button {...props}>{children}</button>;
}
// Usage
<Button onClick={handleClick} disabled={isLoading} className="btn-primary">
Submit
</Button>
// State updates (never mutate state directly!)
setUser(prevUser => ({ ...prevUser, name: "Bob" }));
setItems(prev => [...prev, newItem]);
Rest Parameters
Rest collects remaining arguments into an array. It must be the last parameter:
// Collect extra arguments
function sum(...numbers) {
return numbers.reduce((total, n) => total + n, 0);
}
sum(1, 2, 3); // 6
sum(1, 2, 3, 4, 5); // 15
// Mix regular and rest parameters
function logger(level, ...messages) {
console.log(`[${level}]`, ...messages);
}
logger("ERROR", "File not found", "path:", "/etc/config");
// [ERROR] File not found path: /etc/config
// Rest in arrow functions
const multiply = (factor, ...nums) => nums.map(n => n * factor);
multiply(3, 1, 2, 3, 4); // [3, 6, 9, 12]
Rest in Destructuring
// Array rest
const [first, second, ...rest] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(second); // 2
console.log(rest); // [3, 4, 5]
// Object rest
const { name, age, ...otherProps } = user;
console.log(name, age); // "Alice", 30
console.log(otherProps); // { city: "NYC", role: "admin" }
// Remove a property immutably
function removeKey(obj, keyToRemove) {
const { [keyToRemove]: _, ...remaining } = obj;
return remaining;
}
const noAge = removeKey(user, "age");
// { name: "Alice", city: "NYC", role: "admin" }
Rest vs arguments
Before rest parameters, functions used the arguments object:
// Old way — arguments is array-like, not a real array
function oldSum() {
return Array.from(arguments).reduce((a, b) => a + b, 0);
}
// Modern way — rest gives you a real array immediately
const modernSum = (...nums) => nums.reduce((a, b) => a + b, 0);
// Arrow functions don't have arguments — another reason to use rest
Practical Patterns
// Merge defaults with user options
function createConfig(overrides = {}) {
const defaults = {
timeout: 5000,
retries: 3,
verbose: false,
};
return { ...defaults, ...overrides };
}
createConfig({ timeout: 10000 });
// { timeout: 10000, retries: 3, verbose: false }
// Convert NodeList to array (spread works on iterables)
const divs = [...document.querySelectorAll("div")];
divs.forEach(div => div.classList.add("loaded"));
// Convert string to array of chars
const chars = [..."hello"]; // ["h", "e", "l", "l", "o"]
// Unique values
const unique = [...new Set([1, 2, 2, 3, 3, 3])]; // [1, 2, 3]
Next lesson: Template Literals — powerful string interpolation and tagged templates.
📱
Get Notes Free →Get this course's notes on Telegram!
Free cheat sheets, summaries & practice exercises