Spread vs Rest

In this lesson, we will explore the features represented by three dots in JavaScript, the rest parameter and the spread operator.

JavaScript version ES6 (ES2015) brought us a couple of useful features for arrays represented by three dots ..., the rest parameter and the spread operator. ES2018 introduced the same syntax for objects.

It can be confusing to have one syntax represent two different uses. In this lesson, we will try to clear up the confusion and break down the two ways of using JavaScript’s three dots.

In short, we can say that:

  • The spread operator unpacks elements.
  • The rest parameter packs elements.

Definitions

  • argument – An argument is a value passed to a function.

  • parameter – A parameter is a variable used in the declaration of a function.

  • iterable – An iterable is a collection of something that we can iterate (loop) over, including arrays, lists, sets, and strings.

function print(params) {
  params.forEach(param => console.log(param));
}

print(['arg1', 'arg2']);
// -> arg1
// -> arg2 

Spread operator

The spread operator unpacks iterables into individual elements. Let’s look into different scenarios where this is useful.

Arrays

The spread operator unpacks an array into separate arguments:

const array = [1, 2, 3];
console.log(...array);
// -> 1 2 3

This comes in handy when a function expects a list of arguments and all you have is an array:

const array = [4, 2, 9, 5];

Math.max(...array); // -> 9

To copy an array, we put the spread values into another array:

const arrayA = [1, 2, 3];
const arrayB = [...arrayA];

console.log(arrayB);
// -> [1, 2, 3]

This is a great way to clone arrays. If we change either of the arrays, it will not affect the other.

The spread syntax can also be used to compile several values into one:

const arrayA = [1, 2, 3];
const arrayB = [0, ...arrayA, 4];

console.log(arrayB);
// -> [0, 1, 2, 3, 4]

This is useful when we want to add elements into an existing array. We can even merge two arrays:

const first = [1, 2];
const other = [3, 4];

const combo = [...first, ...other]; 
// [1, 2, 3, 4]

These operations are not only available for arrays but also for other iterables like strings:

const word = 'test';

console.log([...word]);
// -> ["t", "e", "s", "t"]

Objects

With objects, the spread operator (…) is used to create copies of existing objects with new or updated values or to make a copy of an object with more properties. Let’s discuss an example of how to use the spread operator on an object.

Here, we are spreading the user object. All key-value pairs are copied into the clonedUser object.

const user = { 
  name: 'Max', 
  age: 42
}; 

const clonedUser = { ...user }; 

The spread syntax is useful for merging the properties and methods on an object into a new object:

const x = { x: 1 };
const y = { y: 2 };

const coord = {...x, ...y};
console.log(coord);
// {x: 1, y: 2}

Note: The spread syntax only creates a shallow copy so nested arrays or objects will not copy properly. The deeper data is still linked to the original.

Rest parameter

Where the spread operator unpacks the contents of an iterable into single elements, the rest parameter collects all the remaining elements into an array.

In JavaScript, it’s possible to call a function with any number of arguments. We can use the rest parameter when we don’t know how many arguments will be used or when we just want to collect some of them into an array.

Let’s try an example where we add together all the values sent into our function:

function sum(...args) {
  let result = 0;

  for (let arg of args) {
    result += arg;
  }

  return result
}

sum(4, 2) // -> 6
sum(3, 4, 5, 6) // -> 18

So, no matter how many numbers we send into the sum function, they will be added together.

Let’s do another example:

function family(spouse, ...children) {
  console.log(`Spouse: ${spouse}`);

  for(const child of children) {
    console.log(`Child: ${child}`);
  }
}

family('Veronica', 'Max', 'Jack');
// -> Spouse: Veronica
// -> Child: Max
// -> Child: Jack

As shown above, the three dots collect all the elements after the spouse in the children array.

We can also use the rest syntax with objects together with destructuring:

const player = {
  name: 'Max Best',
  age: 42,
  game: 'Football'
}

const { name, ...rest } = player;

console.log(name); // -> Max Best
console.log(rest);
// -> {age: 42, game: 'Football'}

Note: Rest parameters have to be the last argument. This is because they collect all remaining arguments into an array.

Conclusion

  • The rest parameter collects all the remaining elements into an array.

  • The spread operator expands collected elements, such as arrays, into single elements.

  • The spread syntax only creates a shallow copy one level deep.

  • Only the last parameter can be a rest parameter.


Quiz on spread and rest

Get hands-on with 1000+ tech skills courses.