Master the ... syntax for spreading arrays and objects, and collecting function arguments with rest parameters.
The spread operator (...) expands an iterable (like an array or object) into individual elements. It looks the same as the rest operator but works in the opposite direction.
Spread in arrays — concatenate or copy arrays:
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const merged = [...arr1, ...arr2];
console.log(merged); // [1, 2, 3, 4, 5, 6]
const copy = [...arr1];
console.log(copy); // [1, 2, 3] (a new array, not a reference)Spread in function calls — pass array elements as individual arguments:
const numbers = [5, 2, 8, 1, 9];
console.log(Math.max(...numbers)); // 9
// Without spread, you would need:
console.log(Math.max.apply(null, numbers)); // 9 (old way)Spread in object literals — merge or copy objects:
const defaults = { theme: 'light', lang: 'en' };
const userPrefs = { theme: 'dark', fontSize: 16 };
const settings = { ...defaults, ...userPrefs };
console.log(settings);
// { theme: 'dark', lang: 'en', fontSize: 16 }When properties overlap, later values overwrite earlier ones. This makes spread perfect for applying overrides to default settings.
The rest operator also uses ... but does the opposite — it collects multiple elements into a single array.
Rest in function parameters — accept any number of arguments:
function sum(...numbers) {
return numbers.reduce((total, n) => total + n, 0);
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(10, 20, 30, 40)); // 100Unlike the old arguments object, rest parameters produce a real array with all array methods available.
Rest in array destructuring:
const [first, second, ...remaining] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(second); // 2
console.log(remaining); // [3, 4, 5]Rest in object destructuring:
const { name, ...details } = { name: 'Alice', age: 30, city: 'Paris' };
console.log(name); // 'Alice'
console.log(details); // { age: 30, city: 'Paris' }Important rule: The rest element must always be the last element:
const [first, ...rest, last] = [1, 2, 3]; // SyntaxError!Spread and rest enable many useful patterns in everyday JavaScript.
Cloning arrays and objects (shallow copy):
const original = [1, [2, 3], 4];
const clone = [...original];
clone[0] = 99;
console.log(original[0]); // 1 (unaffected)
// Note: nested arrays/objects are still shared (shallow copy)Merging objects with overrides:
const config = {
...defaultConfig,
...userConfig,
timestamp: Date.now() // always override timestamp
};Removing properties from an object without mutation:
const user = { name: 'Alice', password: 'secret', age: 30 };
const { password, ...safeUser } = user;
console.log(safeUser); // { name: 'Alice', age: 30 }
// password is extracted but not included in safeUserCollecting variadic arguments:
const log = (level, ...messages) => {
console.log(`[${level}]`, ...messages);
};
log('INFO', 'Server started', 'on port 3000');
// [INFO] Server started on port 3000Adding elements to an array without mutation:
const todos = ['Buy milk', 'Clean house'];
const updated = [...todos, 'Walk the dog'];
// original todos array is unchanged<div id="output"></div>
<script>
// Spread: merge arrays
const fruits = ['apple', 'banana'];
const veggies = ['carrot', 'pea'];
const food = [...fruits, ...veggies];
// Spread: merge objects
const defaults = { color: 'blue', size: 'medium' };
const custom = { size: 'large', weight: 'bold' };
const merged = { ...defaults, ...custom };
// Rest: variadic function
const sum = (...nums) => nums.reduce((a, b) => a + b, 0);
// Rest: remove property
const user = { name: 'Alice', password: '123', role: 'admin' };
const { password, ...safeUser } = user;
const output = document.getElementById('output');
output.innerHTML = `
<p>Food: [${food.join(', ')}]</p>
<p>Merged: ${JSON.stringify(merged)}</p>
<p>sum(1,2,3,4,5): ${sum(1, 2, 3, 4, 5)}</p>
<p>Safe user: ${JSON.stringify(safeUser)}</p>
`;
</script>What is the difference between spread and rest?