Skip to main content Accessibility Feedback

Rest parameters vs. the spread operator in vanilla JavaScript

One of the more confusing aspects of modern JavaScript development is that both rest parameters and the spread operator use three dots as their operator (...).

Today, we’re going to look at what they both do, how they’re different, and how to tell them apart.

Let’s dig in!

The Spread Operator

The spread syntax operator takes an array or object (or other iterable) and expands its items into their own individual values.

let sandwiches = ['tuna', 'turkey', 'pb&j'];

// logs ["tuna", "turkey", "pb&j"]
console.log(sandwiches);

// logs tuna turkey pb&j
console.log(...sandwiches);

The spread operator can only be used inside of functions, arrays and objects. You cannot use it on its own.

This, for example, would throw an error.

// Uncaught SyntaxError: Unexpected token '...'
...sandwiches;

Here’s a demo of the spread operator.

Rest Parameters

A rest parameter is a function parameter that gets assigned an array with any arguments that are passed in at or after it when a function is called. You define a rest parameter by prefixing it with three dots (...).

In the example below, ...moreArgs is a rest parameter.

function logStuff (arg1, arg2, ...moreArgs) {

	// Logs arg1
	console.log(arg1);

	// Logs arg2
	console.log(arg2);

	// Logs an array of any other arguments you pass in after arg2
	console.log(moreArgs);

}

If you passed in more than two arguments, everything from the third argument and beyond is assigned to moreArgs as an array.

// In this example...
// arg1 = 'chicken'
// arg2 = 'tuna'
// moreArgs = ['chips', 'cookie', 'soda', 'delicious']
logStuff('chicken', 'tuna', 'chips', 'cookie', 'soda', 'delicious');

Here’s a demo of rest parameters.

You can use them together

Where things get really confusing is that you can use rest parameters and the spread operator together.

In the castSpells() function, we’ve declared a rest parameter, moreSpells.

function castSpells (mainSpell, ...moreSpells) {
	console.log(mainSpell);
	console.log(moreSpells);
}

Let’s imagine that you have an array of spells, like this.

let spells = ['Dancing teacups', 'You shall not pass', 'Talk to animals', 'Disappear', 'Fly'];

You could manually pass all of those spells into the castSpells() function, like this…

castSpells(spells[0], spells[1], spell[2], spell[3], spell[4]);

Or, you could use the spread operator to pass them all in as a comma-separated list.

castSpells(...spells);

Here’s a demo of spread and rest parameters together.

This can be a bit confusing, because you have a function and a parameter prefixed with three dots. Then, you’re calling the function with an argument prefixed with three dots.

How do you tell them apart?

There’s a simple trick for telling rest parameters and the spread operator apart.

If you’re declaring a function (either using a traditional function keyword or an arrow function), it’s a rest parameter.

// This is a rest parameter, because you're declaring a function
function castSpells (mainSpell, ...moreSpells) {
	// ...
}

In any other context, it’s a spread operator.

// This is a spread operator, because we're running a function that's already declared
castSpells(...spells);