rest and spread operators (...)

By Mike Sharp
26 March, 2021

Github icon
Back to posts

tldr? Prefer moving pictures instead? we've got you covered, here's the video version of this blog.

What we're going to cover

We're going to look at the Javascript Spread and Rest syntax.

Spread and Rest statements are written using the ellipsis (...) keyword

We're going to see how using Spread and Rest can:

  • Increase your productivity
  • Make your code more concise when working with arrays, objects and even with functions.

So what are spread and rest?

Spread and Rest both use the same ellipsis ... keyword, however, they're distinct Javascript features, so we'll take a look at them individually.

Let's start with Spread and then we'll cover Rest.

The Spread operator

The Spread operator takes a source Array or Object as input and then extracts all of the items in the case of an array, or the properties in the case of an Object and the items or properties are then inserted into a target Object or Array.

Think of using spread as extracting the contents of a data structure in order to insert those contents into a new data structure, like taking the items out of a box and then putting them into a different box.

Let's take a look at this versatile feature in action.

Cloning an array using Spread

In the example below an array is being spread into a brand new array, that is, the contents of originalArray are being put into a brand new array called spreadArray, effectively doing a shallow clone of the array.

// spread into/clone an array
const originalArray = [1, 2, 3];
const spreadArray = [...originalArray];
console.log(spreadArray);

Output:

[1, 2, 3]

Combining arrays together

The spread operator can also be used as a handy way of stitching together the contents of multiple arrays, as demonstrated below.

Arrays can be stitched together into a new array, by spreading the existing arrays at any position in the target array. You can perform as many spreads as you like in one go.

Without the spread operator you would have to do an Array.concat() or use multiple push(), pop(), splice() or slice() method calls to achieve the desired results.

// put at beginning of array
const spreadArrayB = [1,2,3];
const beginningArray = [...spreadArrayB, 'a', 'b', 'c', 'd']; 
console.log(beginningArray);

// put at middle of array
const spreadArrayC = [1,2,3];
const middleArray = ['a', 'b', ...spreadArrayC, 'c', 'd']; 
console.log(middleArray);

// put at end of array
const spreadArrayD = [1,2,3];
const endArray = ['a', 'b', 'c', 'd', ...spreadArrayD]; 
console.log(middleArray);

// concat multiple arrays together 
const arrayA = [1, 2, 3];
const arrayB = [4, 5, 6];
const combinedArray = [...arrayA, ...arrayB];
console.log(combinedArray);

Output:

[1, 2, 3, "a", "b", "c", "d"]
["a", "b", 1, 2, 3, "c", "d"]
["a", "b", 1, 2, 3, "c", "d"]
[1, 2, 3, 4, 5, 6]
["a", "b", "c", "d"]

Cloning objects

Cloning objects has fortunately become a lot simpler in modern Javascript, with the introduction of Object.Assign() (in the old days you would need to loop over all of the keys of the object and do the copying manually, madness!), but with the spread operator it's even easier to perform a quick shallow clone of an object as shown below.

// Clone object using Object.Assign
const newObjectWithObjectClone = Object.assign({}, originalObject);
console.log(newObjectWithObjectClone);

// Clone object with spread
const originalObject = {a: 1, b: 2, c: 3};
const newObject = {...originalObject};
console.log(newObject)

Output:

{a: 1, b: 2, c: 3}
{a: 1, b: 2, c: 3}

Merging objects together

You can just as easily merge together objects as you can make copies using the spread operator.

The spread operator will merge objects that you spread into a single object.

In the case where a property already exists, the right-most spread objects property will take prescedence.

In the example below, you can see that combinedPizza becomes a large pepperoni pizza, because pizzaB is spread after pizzaA when combinedObject is defined and it is pizzaB that has the large size property, which takes prescedence since pizzaB is merged last.

// Combine object
const personObject = {name: 'Mike', age: 30};
const likeObject = {food: 'Pizza', colour: 'Green'}; 

// combine two objects
const combinedObject = {...personObject, ...likeObject};
console.log(combinedObject)

// merge two objects
const pizzaA = {size: 'small', flavour: 'pepperoni'}
const pizzaB = {size: 'large', flavour: 'pepperoni'}
const combinedPizza = {...pizzaA, ...pizzaB};
console.log(combinedPizza)

const combinedUsingAssignPizza = Object.assign(pizzaA, pizzaB);
console.log(combinedUsingAssignPizza);

Output:

{size: "large", flavour: "pepperoni"}
{size: "large", flavour: "pepperoni"}

Spreading function arguments

You can also take an array and spread the contents into arguments in a function.

In the example below, the sumThreeNumbers takes three arguments in the functions signature, instead of writing those values one at a time we can simply spread an array of arguments into the argument list when the function is called.

You would have had to use function.prototype.apply() or function.prototype.call() prior to the introduction of spread to achieve the same results, but not any more, fortunately.

// spread args into function
function sumThreeNumbers(a, b, c) {
    return a + b + c;
}

// provide arguments as array
const args = [1, 2, 3]; 
console.log(sumThreeNumbers(...args));

// non-spread alternative
console.log(sumThreeNumbers.apply(this, args));

Output:

6

In a nutshell, spreading is great!

The Spread operator is simple to use and very handy for solving many common problems with cloning and combining Arrays, Objects, and using arrays as function arguments.

With all of that in mind, let's take a look at how we can use the ellipsis keyword, in another closely related but different way using Rest syntax.

Rest Parameters using the Rest operator

Rest syntax (also known as Rest Parameters) is a Javascript feature that lets you specify a single parameter in a function which represents infinite parameters when the function is called.

The simplest way to think about a function that uses a Rest parameter is that when it gets called, any excess or remaining arguments unhandled in the function signature that get passed into the function are converted into an array containing those remaining arguments.

The array of arguments can then referenced inside the function.

Here's an example example demonstrating Rest syntax in action.

Code:

function consumeRestArgs(argA, argB, ...args) {
    // log first arg
    console.log(argA);
    // log second arg
    console.log(argB);
    // log all other args 
    args.map(val => console.log(val));
}
consumeRestArgs('a', 'b', 'c', 'd', 'e');

Output:

 a
 b
 c
 d
 e

Infinite arguments can be passed into consumeRestArgs and any which aren't handled explicitly are dropped into an arguments array, which can then be iterated like a regular array of items.

All arguments specified before the rest argument are treated as regular arguments (in this case argA and argB).

When using rest arguments, you must remember to only specify rest arguments as the last argument in the function signature.

Rest in summary

Rest syntax is a great way to handle function calls with an indeterminate number of arguments on each call.

Rest syntax adds flexibility to your functions simply and intuitively.

Final Gotchas

The great news is, there aren't any trade-offs for using Rest or Spread, they're both well supported in latest builds of all popular browsers.

However, Rest and Spread cannot be Polyfilled, so you will need to use a transpiler like Babel in your build process if you want to avoid running into Javascript errors in older browsers such as Internet Explorer.

It's best to use Rest and Spread natively if you don't need to support legacy browsers.

Don't take my word for it, I'll let you do the rest... so spread the word!

So now you've got a couple of extra Javascript tools at your disposal, I hope you enjoy putting them to good use.

Start using Spread and Rest syntax in your Javascript today!

If you liked this blog, there's also a video version here