Skip to main content
Madhukar
All Articles

Spread vs Rest Operators in JavaScript

August 8, 20266 min read
javascriptes6web-developmentinterview-prep
Spread vs Rest Operators in JavaScript

Same three dots, two opposite jobs

... shows up in two completely different roles in JavaScript, and confusing them is one of the most common early mixups. Both look identical on the page — three dots — but one expands a collection into individual pieces, and the other collects individual pieces back into a single array. This guide draws a clear, lasting line between the two.

What the Spread Operator Does

Spread syntax expands an iterable into individual values in array literals, function calls, and other supported contexts.

const numbers = [1, 2, 3];

console.log(...numbers); // 1 2 3 — not "[1, 2, 3]"

Notice console.log(...numbers) doesn't print the array itself — it prints each individual number, spread out as separate arguments. That's the core idea: In iterable contexts, spread takes one collection and expands it into individual values. Object spread is different: it copies an object's enumerable own properties into a new object.

What the Rest Operator Does

Rest does the exact opposite — it takes multiple individual values and collects them into a single array.

function sum(...numbers) {
console.log(numbers); // [1, 2, 3] — collected into one array
}

sum(1, 2, 3);

Here, ...numbers in the function's parameter list gathers however many arguments were actually passed into one real array, called numbers, inside the function.

Differences Between Spread and Rest

A genuinely reliable way to tell them apart at a glance: if ... appears on the value being provided (an existing array, an existing object), it's spread. If it appears on the parameter or variable receiving values, it's rest.

const arr = [1, 2, 3];
console.log(...arr); // spread — arr already exists, being expanded

function example(...args) {} // rest — args doesn't exist yet, being collected

Using Spread With Arrays and Objects

Combining arrays

const fruits = ["apple", "banana"];
const vegetables = ["carrot", "potato"];

const groceries = [...fruits, ...vegetables];
console.log(groceries); // ["apple", "banana", "carrot", "potato"]

Copying an array

const original = [1, 2, 3];
const copy = [...original];

copy.push(4);
console.log(original); // [1, 2, 3] — untouched
console.log(copy); // [1, 2, 3, 4]

Spreading into a new array creates a genuinely separate copy — modifying copy doesn't affect original, unlike simply writing const copy = original, which would just point both variables at the exact same array.

Combining objects

const personalInfo = { name: "Priya", age: 21 };
const contactInfo = { email: "priya@example.com" };

const fullProfile = { ...personalInfo, ...contactInfo };
console.log(fullProfile);
// { name: "Priya", age: 21, email: "priya@example.com" }

Copying and updating an object

const user = { name: "Aarav", role: "user" };

const updatedUser = { ...user, role: "admin" };
console.log(updatedUser); // { name: "Aarav", role: "admin" }
console.log(user); // { name: "Aarav", role: "user" } — unchanged

This pattern — spread the original, then override specific properties — is extremely common for updating an object without mutating the original, which matters a lot in frameworks like React, where state is expected to be replaced, not directly modified.

Practical Use Cases

Passing an array’s values as individual function arguments

function add(a, b, c) {
return a + b + c;
}

const nums = [1, 2, 3];
console.log(add(...nums)); // 6 — spreads the array into three separate arguments

Collecting extra function arguments

function logAll(first, ...others) {
console.log("First:", first);
console.log("Others:", others);
}

logAll("a", "b", "c", "d");
// First: a
// Others: ["b", "c", "d"]

first captures just the first argument; ...others (rest) gathers everything remaining into an array — a common pattern when a function needs to handle a fixed first argument plus a flexible, variable number of additional ones.

Destructuring with rest

const { name, ...rest } = { name: "Meera", age: 22, city: "Pune" };

console.log(name); // "Meera"
console.log(rest); // { age: 22, city: "Pune" }

rest here collects every property except name into a new object — useful for pulling out one specific value while keeping "everything else" grouped together.

Merging default and custom settings

const defaultSettings = { theme: "light", fontSize: 14 };
const userSettings = { fontSize: 18 };

const finalSettings = { ...defaultSettings, ...userSettings };
console.log(finalSettings); // { theme: "light", fontSize: 18 }

Spread makes it easy to layer custom overrides on top of sane defaults — later spread values override earlier ones for any matching key, which is exactly why userSettings comes second here.

Final Takeaway

Spread and rest share the exact same syntax because they’re really two faces of the same idea, pointed in opposite directions. Spread takes something already grouped together and pulls it apart into individual pieces — copying arrays, merging objects, or passing an array’s values as separate function arguments. Rest takes individual, separate values and gathers them back into one array — handling a flexible number of function arguments, or splitting “this one property” from “everything else” during destructuring. Once you’re reliably asking “is this expanding a collection, or gathering loose values into one?”, telling them apart stops being a guessing game.

Frequently Asked Questions

Can spread and rest be used in the same line of code?

> Yes — for example, const [first, ...rest] = [...combinedArray] uses spread to build combinedArray into a new array, and rest to then destructure it into first and the remaining rest. They're commonly combined once each is individually understood.

Does spreading an array or object create a deep copy?

> No — spread creates a shallow copy. Top-level properties are copied independently, but if a property’s value is itself an object or array, both the original and the copy still reference that same nested object underneath.

Can rest parameters be used anywhere in a function’s parameter list?

> No — a rest parameter must be the last parameter in the list, since it collects “everything remaining.” Placing anything after it would be ambiguous and is not allowed by JavaScript’s syntax rules.

Is the spread operator only for arrays?

> No — while spread originated with arrays, it also works with objects (since ES2018) and with any iterable value, like strings ([...\"abc\"] produces [\"a\", \"b\", \"c\"]).

Originally published by Mr Madhukar

Read the complete article on Medium with full formatting & reader responses.