Docs
README
3.7 Spread Operator
Table of Contents
- ā¢What Is the Spread Operator
- ā¢Spread with Arrays
- ā¢Spread with Objects
- ā¢Spread with Function Calls
- ā¢Spread vs Rest
- ā¢Common Use Cases
- ā¢Performance Considerations
What Is the Spread Operator
The spread operator (...) allows an iterable (like an array) or an object to be expanded in places where multiple elements or properties are expected.
Syntax
// Array spread
const newArray = [...existingArray];
// Object spread
const newObject = { ...existingObject };
// Function call spread
functionName(...args);
Visual Representation
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā SPREAD OPERATOR ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā INPUT: [1, 2, 3] or { a: 1, b: 2 } ā
ā ā ā ā
ā SPREAD: ...arr ...obj ā
ā ā ā ā
ā OUTPUT: 1, 2, 3 a: 1, b: 2 ā
ā (individual (individual ā
ā elements) properties) ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Spread with Arrays
Copying Arrays
const original = [1, 2, 3];
const copy = [...original];
console.log(copy); // [1, 2, 3]
console.log(copy === original); // false (new array)
Concatenating Arrays
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
// Using spread
const combined = [...arr1, ...arr2];
console.log(combined); // [1, 2, 3, 4, 5, 6]
// Equivalent to
const combined2 = arr1.concat(arr2);
Adding Elements
const numbers = [2, 3, 4];
// Add at beginning
const withStart = [1, ...numbers]; // [1, 2, 3, 4]
// Add at end
const withEnd = [...numbers, 5]; // [2, 3, 4, 5]
// Add at both
const withBoth = [0, ...numbers, 5]; // [0, 2, 3, 4, 5]
// Insert in middle
const withMiddle = [...numbers.slice(0, 2), 2.5, ...numbers.slice(2)];
// [2, 3, 2.5, 4]
Spreading Strings
const word = 'Hello';
const letters = [...word];
console.log(letters); // ['H', 'e', 'l', 'l', 'o']
Converting Iterables
// Set to Array
const set = new Set([1, 2, 3]);
const arrFromSet = [...set]; // [1, 2, 3]
// Map keys/values to Array
const map = new Map([
['a', 1],
['b', 2],
]);
const keys = [...map.keys()]; // ['a', 'b']
const values = [...map.values()]; // [1, 2]
// NodeList to Array (in browser)
// const divs = [...document.querySelectorAll('div')];
Shallow Copy Warning
const original = [
[1, 2],
[3, 4],
];
const copy = [...original];
// The inner arrays are still referenced, not copied!
copy[0].push(99);
console.log(original[0]); // [1, 2, 99] - Original affected!
Spread with Objects
Copying Objects
const original = { a: 1, b: 2 };
const copy = { ...original };
console.log(copy); // { a: 1, b: 2 }
console.log(copy === original); // false (new object)
Merging Objects
const defaults = { theme: 'light', language: 'en' };
const userPrefs = { theme: 'dark' };
const settings = { ...defaults, ...userPrefs };
console.log(settings); // { theme: 'dark', language: 'en' }
// Later spread overwrites earlier values
Adding/Overriding Properties
const user = { name: 'John', age: 30 };
// Add new property
const withEmail = { ...user, email: 'john@example.com' };
// { name: 'John', age: 30, email: 'john@example.com' }
// Override existing property
const older = { ...user, age: 31 };
// { name: 'John', age: 31 }
// Order matters!
const younger = { age: 25, ...user };
// { age: 30, name: 'John' } - user.age overwrites 25
Excluding Properties
const user = { name: 'John', password: 'secret', age: 30 };
// Using destructuring with rest to exclude
const { password, ...safeUser } = user;
console.log(safeUser); // { name: 'John', age: 30 }
Conditional Properties
const isAdmin = true;
const user = {
name: 'John',
...(isAdmin && { role: 'admin' }),
// or: ...(isAdmin ? { role: 'admin' } : {})
};
console.log(user); // { name: 'John', role: 'admin' }
Nested Object Warning
const original = {
name: 'John',
address: { city: 'NYC', zip: '10001' },
};
const copy = { ...original };
copy.address.city = 'LA';
console.log(original.address.city); // 'LA' - Original affected!
Spread with Function Calls
Passing Array Elements as Arguments
const numbers = [1, 5, 3, 9, 2];
// Without spread - won't work as expected
console.log(Math.max(numbers)); // NaN
// With spread - each element becomes an argument
console.log(Math.max(...numbers)); // 9
console.log(Math.min(...numbers)); // 1
Before Spread (ES5)
// Old way using apply
Math.max.apply(null, numbers); // 9
// New way with spread
Math.max(...numbers); // 9
Multiple Spreads
const arr1 = [1, 2];
const arr2 = [5, 6];
console.log(Math.max(...arr1, 3, 4, ...arr2)); // 6
With Custom Functions
function greet(first, middle, last) {
return `Hello, ${first} ${middle} ${last}!`;
}
const nameParts = ['John', 'William', 'Doe'];
console.log(greet(...nameParts)); // "Hello, John William Doe!"
Spread vs Rest
Key Difference
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā SPREAD: Expands an array/object into individual elements ā
ā REST: Collects individual elements into an array/object ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā SPREAD (expanding): ā
ā const arr = [1, 2, 3]; ā
ā console.log(...arr); // 1 2 3 ā
ā const newArr = [...arr, 4]; // [1, 2, 3, 4] ā
ā ā
ā REST (collecting): ā
ā function sum(...numbers) { ā
ā // numbers is [1, 2, 3] ā
ā } ā
ā sum(1, 2, 3); ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Context Determines Behavior
// SPREAD - in array/object literals or function calls
const copy = [...original]; // Spread in array literal
const merged = { ...obj1, ...obj2 }; // Spread in object literal
func(...args); // Spread in function call
// REST - in function parameters or destructuring
function fn(...rest) {} // Rest in parameter
const [first, ...rest] = array; // Rest in destructuring
const { a, ...rest } = object; // Rest in destructuring
Common Confusion Example
// This is SPREAD - expanding array into function arguments
const nums = [1, 2, 3];
Math.max(...nums);
// This is REST - collecting arguments into array
function sum(...nums) {
return nums.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3);
Common Use Cases
1. Immutable Updates (React State)
// Original state
const state = {
user: { name: 'John', age: 30 },
items: [1, 2, 3],
};
// Update user name (immutably)
const newState = {
...state,
user: { ...state.user, name: 'Jane' },
};
// Add item (immutably)
const stateWithItem = {
...state,
items: [...state.items, 4],
};
2. Array Deduplication
const numbers = [1, 2, 2, 3, 3, 3, 4];
const unique = [...new Set(numbers)];
console.log(unique); // [1, 2, 3, 4]
3. Converting Arguments to Array
function legacyFunction() {
// arguments is array-like but not an array
const args = [...arguments];
return args.map((x) => x * 2);
}
// Modern way: use rest parameters instead
function modernFunction(...args) {
return args.map((x) => x * 2);
}
4. Clone and Modify
const original = { a: 1, b: 2 };
// Clone and add
const withC = { ...original, c: 3 };
// Clone and remove (using destructuring)
const { b, ...withoutB } = original;
// Clone and modify
const modified = { ...original, a: 100 };
5. Combine Arrays Conditionally
const base = ['a', 'b'];
const extra = ['c', 'd'];
const includeExtra = true;
const result = [...base, ...(includeExtra ? extra : [])];
console.log(result); // ['a', 'b', 'c', 'd']
6. Function Composition
function pipe(...fns) {
return (value) => fns.reduce((acc, fn) => fn(acc), value);
}
const double = (x) => x * 2;
const addTen = (x) => x + 10;
const process = pipe(double, addTen);
console.log(process(5)); // 20 (5*2 + 10)
Performance Considerations
When Spread is Efficient
// Good: Small arrays/objects
const small = [...[1, 2, 3]];
// Good: One-time operations
const merged = { ...defaults, ...options };
When to Be Careful
// Careful: Very large arrays
const huge = new Array(1000000).fill(0);
const copy = [...huge]; // Creates new million-element array
// Careful: In hot loops
for (let i = 0; i < 1000; i++) {
array = [...array, newItem]; // Bad! O(n²) complexity
}
// Better: Use push or concat for multiple additions
const items = [];
for (let i = 0; i < 1000; i++) {
items.push(newItem); // O(n) total
}
Deep Clone Alternative
// Spread is shallow - for deep clone use:
const deepCopy = JSON.parse(JSON.stringify(original));
// Or structured clone (modern browsers)
const deepCopy = structuredClone(original);
Summary
| Use Case | Spread Syntax | Result |
|---|---|---|
| Copy array | [...arr] | New array with same elements |
| Merge arrays | [...a, ...b] | Combined array |
| Copy object | {...obj} | New object with same properties |
| Merge objects | {...a, ...b} | Combined object (b overwrites a) |
| Function args | fn(...arr) | Array elements as separate args |
| String to array | [...str] | Array of characters |
| Set to array | [...set] | Array from Set |
Key Points
- ā¢Spread expands iterables/objects
- ā¢Creates shallow copies (nested objects are still referenced)
- ā¢Later values override earlier ones in object spread
- ā¢Works with any iterable for arrays
- ā¢Often used for immutable updates
Next Steps
After mastering the spread operator, proceed to:
- ā¢3.8 Rest Operator - The counterpart that collects elements
- ā¢Practice immutable state updates
- ā¢Combine with destructuring for powerful patterns