Spread vs Rest Operators in JavaScript

Before ES6 (2015), JavaScript required verbose and manual methods for copying arrays or handling multiple function arguments. The introduction of Spread and Rest operators simplified these common tasks using a single, intuitive syntax: three dots (...).
What spread operator does
The spread operator spreads or expands the iterable into individual elements.
const arr1 = [1,2,3]
const arr2 = [4,5,6]
const merged = [...arr1 , ...arr2]
console.log(merged) // [1,2,3,4,5,6]
Before this it was not so easy to merged the array or object properties, you have to concat or merged the arrays.
Arrays: Merging and Copying
Before ES6, you had to use .concat() or .slice(). Now, it’s much cleaner.
const fruits = ['apple', 'banana'];
const veggies = ['carrot', 'potato'];
// MERGING: Combining two arrays into one
const groceryList = [...fruits, ...veggies, 'milk'];
console.log(groceryList); // ['apple', 'banana', 'carrot', 'potato', 'milk']
// COPYING: Creating a shallow copy (changes to copy won't affect original)
const fruitsCopy = [...fruits];
fruitsCopy.push('mango');
console.log(fruits); // ['apple', 'banana']
console.log(fruitsCopy); // ['apple', 'banana', 'mango']
Objects: Copying and Updating
This is the most common way to handle state in modern frameworks like React.
const user = { name: 'Aryan', age: 25, city: 'Delhi' };
// CLONING & UPDATING: Copying all properties and changing one
const updatedUser = { ...user, age: 26 };
console.log(user); // { name: 'Aryan', age: 25, city: 'Delhi' }
console.log(updatedUser); // { name: 'Aryan', age: 26, city: 'Delhi' }
// MERGING: Combining two objects
const contact = { email: 'aryan@example.com' };
const fullProfile = { ...user, ...contact };
console.log(fullProfile); // { name: 'Aryan', age: 25, city: 'Delhi', email: 'aryan@example.com' }
Function Calls: Individual Arguments
When you have an array, but the function expects individual comma-separated values.
const numbers = [10, 20, 30];
function calculateVolume(length, width, height) {
return length * width * height;
}
// Without Spread: calculateVolume(numbers[0], numbers[1], numbers[2])
// With Spread:
const volume = calculateVolume(...numbers);
console.log(volume); // 6000
What Rest operator does
The Rest operator does the opposite: it gathers or "collects" multiple individual elements into a single array.
function sum(...numbers) {
let total = 0;
// The 'numbers' parameter is an array, so we can use array methods
for (const num of numbers) {
total += num;
}
return total;
}
console.log(sum(1, 2, 3)); // Output: 6
console.log(sum(10, 20, 30, 40)); // Output: 100
Rest Parameters (Function Definitions)
This allows a function to accept any number of arguments. It gathers them into a real array so you can use methods like .map() or .reduce().
// 'numbers' collects all incoming arguments into an array
function multiplyAll(multiplier, ...numbers) {
return numbers.map(num => num * multiplier);
}
const result = multiplyAll(2, 5, 10, 15);
// 2 is 'multiplier', [5, 10, 15] is 'numbers'
console.log(result); // [10, 20, 30]
Array Destructuring
The Rest operator must be the last element in the destructuring pattern. It collects the "leftover" elements.
const colors = ['red', 'green', 'blue', 'yellow', 'purple'];
// Extracting the first two, and "gathering" the rest
const [primary, secondary, ...otherColors] = colors;
console.log(primary); // "red"
console.log(secondary); // "green"
console.log(otherColors); // ["blue", "yellow", "purple"]
Object Destructuring
Very useful for removing specific properties from an object while keeping the others intact.
const laptop = {
brand: 'Apple',
model: 'MacBook Air',
year: 2024,
processor: 'M3'
};
// Extract 'brand', and put the "remaining" properties into 'specs'
const { brand, ...specs } = laptop;
console.log(brand); // "Apple"
console.log(specs); // { model: 'MacBook Air', year: 2024, processor: 'M3' }
Combining Rest with Functions (Modern Pattern)
This is frequently used in modern JavaScript (like React) to pass down "the rest" of the props.
function createUser(name, email, ...additionalInfo) {
return {
name,
email,
metadata: additionalInfo // This is now an array of extra details
};
}
const user = createUser('Alice', 'alice@test.com', 'Admin', 'Logged-In', 'Premium-User');
console.log(user.metadata); // ["Admin", "Logged-In", "Premium-User"]
Differences between spread and rest
| Feature | Spread Operator | Rest Operator |
|---|---|---|
| Main Action | Unpacks elements (expands). | Packs elements (collects). |
| Typical Context | Function calls, array/object creation. | Function definitions, destructuring. |
| Example Use | const copy = [...original] |
function fn(first, ...rest) {} |
Note: The Rest operator must always be the last parameter in a function definition.
Conclusion
In conclusion, while the Spread and Rest operators share the same ... syntax, they serve opposite purposes in modern JavaScript. The Spread operator is used to expand or "unpack" elements from arrays and objects, making it the go-to tool for merging data or passing arguments to functions. On the other hand, the Rest operator is used to collect or "pack" multiple elements into a single array, simplifying how we handle indefinite function parameters and variable destructuring.
Mastering these operators is essential for writing cleaner, more readable, and efficient code, especially when working with modern frameworks like React or Node.js. By replacing clunky ES5 methods like .concat() and arguments with these elegant ES6 features, you can significantly reduce boilerplate and improve your development workflow.






