DEV Community

Cover image for ES6 - A beginners guide - Rest and Spread Operator
Stefan Wright
Stefan Wright

Posted on • Updated on

ES6 - A beginners guide - Rest and Spread Operator

In this article I am going to look into the Rest parameter and Spread operator introduced with ES6. The purpose of these two new entries into the JS specification is to help with condensing the volume of code we write and improve readability. rest is used as a parameter in a function declaration to condense an indeterminate number of parameters into a single array, whereas spread is used in the opposite sense in that we can split iterables (arrays/objects/strings) into individual arguments. Let's at these seperately with some code examples shall we?

Rest

How is ...rest done in ES5?

Typical in ES5 we were quite restricted in the supply of parameters to a function, for example:

function add(a, b){
    return a + b;
}
console.log(add(1, 2)) // Returns 3 in a console.log
Enter fullscreen mode Exit fullscreen mode

If we needed to handle an indeterminate amount of arguments we could reference the arguments keyword:

function newFunc() {
    return arguments;
}
console.log(newFunc('Stefan', 'is', 'coding')) // Returns {0: "Stefan", 1: "is", 2: "coding"}
Enter fullscreen mode Exit fullscreen mode

There is a problem with this approach, the return of the arguments keyword is an array-like object. Unfortunately this means we cannot use array methods such as .filter, or .map. Also, if we were to try and combine the arguments keyword and an ES6 arrow function it would not work because arrow functions do not contain their own arguments binding, this leads to the introduction of the ...rest implementation.

So... how to use ...rest in ES6

As mentioned previously ...rest is used when we do not know the number of parameters that we want to handle in a function declaration. This can be used nicely for math type functions (when we utilise some of the Array helpers introduced with ES5, I am going to cover them later in the series), for example:

function add(...numbers){
    return numbers.reduce((sum, number) => sum + number, 0)
}
console.log(add(1,2,3,4)); // Returns 10 in a console.log
console.log(add(1,2,3,4,5,6,7,8,9,10)); // Returns 55 in a console.log
Enter fullscreen mode Exit fullscreen mode

We might already have some parameters that will always be supplied that we want to reference by name if so we can still declare tham as parameters and use ...rest to automatically use the rest of the parameters. For example here is a very simple example where we still want to have num1 and num2 as named parameters and we'll use that for the starting value in our .reduce helper, but also we can have reference to the rest of the parameters:

function add(num1, num2, ...numbers){
    return numbers.reduce((sum, number) => sum + number, num1 + num2)
}
console.log(add(1,2,3,4)); // Returns 10 in a console.log
console.log(add(1,2,3,4,5,6,7,8,9,10)); // Returns 55 in a console.log
Enter fullscreen mode Exit fullscreen mode

One thing to note though, ...rest parameters must appear at the end of the list of parameters. Attempting to place anything after the ...rest parameter will trigger an Uncaught SyntaxError in your code.

Spread

So, as I mentioned earlier ...spread is used to split iterables (arrays/objects/strings) into a list of agruments, it can also be used to combine multiple arrays into one single array. Let's take a look:

The ES5 way

var arr1 = [1,2,3];
var arr2 = [4,5,6];
// Concatenate an array
var arr3 = arr1.concat(arr2);
console.log(arr3) // Returns [1, 2, 3, 4, 5, 6] in a console.log

// Copying an array
var arr4 = arr2;
console.log(arr4) // Returns [4, 5, 6] in a console.log
// Note: there is a risk when copying an array in this manner, see explanation after this code block

// Expanding an array
var arr5 = [1,2,3];
var expanded = [arr5, 4, 5];
console.log(expanded) // Returns [[1, 2, 3], 4, 5] in a console.log

// String to Array
var string = "stefan";
var split = string.split("")
console.log(split) // Returns ['s', 't', 'e', 'f', 'a', 'n'] in a console.log

// Math functions
var max = Math.max(arr1);
console.log(max) // Returns NaN in a console.log
Enter fullscreen mode Exit fullscreen mode

In the "copying" example I noted that that way of work is susceptible to error, the reason for this is that in ES5 when you "copy" an array you are actually copying the reference to it, so if you update your "new" variable, you will actually update both copies of the array. Let me show you an example:

var arr1 = [1,2,3];
var arr2 = arr1;
arr2.push(4);
console.log(arr1); // Returns [1, 2, 3, 4] in a console.log
console.log(arr2); // Returns [1, 2, 3, 4] in a console.log
Enter fullscreen mode Exit fullscreen mode

So how does ES6 ...spread help?

Using the ...spread operator in ES6 we can create a new list of arguments. This allows us to always treat the new var/let/const as a completely new item. Let's take a look at some of the above examples again in ES6 using ...spread:

let arr1 = [1,2,3];
let arr2 = [4,5,6];

// Concatenate an array
let arr3 = [...arr1, arr2];
console.log(arr3) // Returns [1, 2, 3, 4, 5, 6] in a console.log
// Note, the spread operator is ok in an example like this, but it is not recommended in potentially large application as it can cause excessive memory usage and risks of Stack Overflow errors. Using .concat is safer here

// Copying an array
let arr4 = [...arr2];
console.log(arr4) // Returns [4, 5, 6] in a console.log

// Expanding an array
let arr5 = [1,2,3];
let expanded = [...arr5, 4, 5];
console.log(expanded) // Returns [1, 2, 3, 4, 5] in a console.

// String to Array
let string = "stefan";
let split = [...string]
console.log(split) // Returns ['s', 't', 'e', 'f', 'a', 'n'] in a console.log

// Math functions
let max = Math.max(...arr1);
console.log(max) // Returns 3 in a console.log
Enter fullscreen mode Exit fullscreen mode

Notice how the Math function now returns the value we expected?? Thats because instead of passing an array now (which ES5 would have done) we are passing 1, 2, 3 so the function actually compiles like this:

let arr1 = [1, 2, 3];
// ...arr1 outputs 1, 2, 3
let max = Math.max(1, 2, 3);
Enter fullscreen mode Exit fullscreen mode

Oldest comments (0)