What is Rest and Spread Operator in JavaScript?

Rest and Spread are operators introduced in ES6. Both Rest and Spread syntax is represented by three ellipses or dots followed by a variable. So how do you distinguish between Rest and Spread Operator? It depends upon where it is used. If you saw variable with preceding three dots (e.g …var1) at parameter position of function definition then it is Rest Operator. If it is present in any other settings then it is Spread Operator.

What is Rest Operator?

function evenNum(a,b,c){
  console.log(a);
  console.log(b);
  console.log(c);
}

evenNum(2,4,6,8,10);
Output:
2
4
6

/* 8 and 10 are ignored */

Here in our code example, we have defined function evenNum with three parameters and invoked it with five arguments.

When number of arguments we provide is greater than number of parameters, the additional arguments get ignored.

In our code example arguments 8 and 10 are ignored but with rest operator you can collect any additional arguments in an array.

function evenNum(a, b, …c){
  console.log(a);
  console.log(b);
  console.log(c);
}

evenNum(2,4,6,8,10);

Output:
2
4
[ 6, 8, 10 ]

Rest operator also allows to collect any number of arguments in an array. This can be very useful if you don’t know how many arguments a function will take.

function evenNum( …even ){
  console.log(even);
}

evenNum(2,4,6,8,10,12,14);

Output:
[ 2,  4,  6, 8,10, 12, 14 ]

Note that the rest operator should be placed only on last parameter otherwise it will give syntax error.

function evenNum( …c, a, b){
  console.log(a);
  console.log(b);
  console.log(c);
}

Output:
Rest parameter must be last formal parameter.

Rest parameter must be last formal parameter.

Also, you can only have one rest parameter in function definition.

function evenNum( …c, …a, b){
  console.log(a);
  console.log(b);
  console.log(c);
}

Output:
Rest parameter must be last formal parameter

What is Spread Operator?

It is operator that expands iterable such as arrays and strings into individual elements.

Combining arrays

const evenNum1 = [ 2, 4, 6 ];

const evenNum2 = [ 8, 10, 12 ];

const evenNum3 = [ 14, 16, 18 ];

const evenNum = […evenNum1,…evenNum2 ,…evenNum3 ]

console.log(evenNum);

Output:
[ 2,  4,  6,  8, 10, 12, 14, 16, 18 ]

Here we merge array elements of evenNum1, evenNum2 and evenNum3 to a new array evenNum.

Copying Array

Another use of spread operator for array literal is copying array. But first let’s see the example of copying (or cloning) array without spread operator.

const superhero = [ 'hulk', 'thor', 'ironman'];

const avengers = superhero;

console.log(superhero);
// [ 'hulk', 'thor', 'ironman' ]

console.log(avengers);
// [ 'hulk', 'thor', 'ironman' ]

Here variable avengers is a copy of array superhero. When you copy array this way both arrays will point to same reference in memory.  So when you mutate one array, changes will occur in other array too.

const superhero = [ 'hulk', 'thor', 'ironman'];

const avengers = superhero;

avengers[3] = 'spiderman';

console.log(superhero);
// [ 'hulk', 'thor', 'ironman', 'spiderman' ]

console.log(avengers);
// [ 'hulk', 'thor', 'ironman', 'spiderman' ]

Here we added element to avengers array (copied array) only but superhero array (original array) was also modified.

There is a way to create unique arrays without spread operator also. It is bit complicated.

const superhero = [ 'hulk', 'thor', 'ironman'];

const avengers = superhero.slice.call(superhero);

avengers[3] = 'spiderman';

console.log(superhero);
// [ 'hulk', 'thor', 'ironman' ]

console.log(avengers);
//[ 'hulk', 'thor', 'ironman', 'spiderman' ]

But with the introduction of spread operator it is so much easier to copy arrays with it’s own unique reference in memory.

const superhero = [ 'hulk', 'thor', 'ironman'];

const avengers = […superhero];

avengers[3] = 'spiderman';

console.log(superhero);
// [ 'hulk', 'thor', 'ironman' ]

console.log(avengers);
//[ 'hulk', 'thor', 'ironman', 'spiderman' ]

Here array superhero is unaffected by any changes to avengers array.

Function call

function multiplyNum(a,b,c){
  return a*b*c;
}

const evenNum = [22, 46, 14];

multiplyNum(evenNum);

Output:
NaN

Here we have a function multiplyNum that takes three parameters and returns the product of those parameters. When we pass array evenNum at function call we get NaN because it is expecting individual number as arguments not an array. So the solution is to apply spread syntax at function call. It will expand all the array elements into individual argument.

function multiplyNum(a,b,c){
  return a*b*c;
}

const evenNum = [22, 46, 14];

multiplyNum( …evenNum );

Output:
14168

Copying properties of an object

const anime = {
  country: 'japan',
  isFun: null
}

const dragonBall = {
  …anime,
  character: 'goku',
  studio: 'toei animation',
  isFun: true
}

Here we are copying properties of anime object to dragonBall object. If you notice one of the property of anime object is isFun which dragonBall object already has. So will dragonBall object contain two same properties key name?

In javascript if object has two properties with same key, they will overwrite each other.

How about the value of isFun? Will it be  null (anime object) or true (dragonBall object)? It depends on the order of properties. In our example, isFun: true is written after …anime. We first copied properties of anime object and set the value of key isFun. Any property with same key name will overwrite the value of that property name which appeared before it. So the value of isFun is true.

console.log(dragonBall);

Output:

{
  country: 'japan',
  isFun: true,
  character: 'goku',
  studio: 'toei animation'
}

If we changed the order in dragonBall object like below, the value of isFun will be null.

const dragonBall = {
  character: 'goku',
  studio: 'toei animation',
  isFun: true,
  …anime,
}

console.log(dragonBall);
{
  character: 'goku',
  studio: 'toei animation',
  isFun: null,
  country: ‘japan’
}

Merge Objects

With spread operator you can merge two objects into new object.

const chess = {
  name: 'chess',
  player1: 'active',
}

const checker = {
  name: 'checkers',
  player2: 'active',
}

const boardGame = {…chess, …checker };

console.log(boardGame);
Output:
{ name: 'checkers', player1: 'active', player2: 'active' }

Just as explained above if there are two conflicting property key name, the latter property key  will overwrite the preceding property key value.

Since in our code example checker object comes after chess object, checker key name: ‘checkers’ will overwrite chess object key name: ‘chess’ .