Follow AiTechWorlds on LinkedIn for professional AI content!Follow Now →
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 this course's notes on Telegram!

Free cheat sheets, summaries & practice exercises

Get Notes Free →
!