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
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"}
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
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
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
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
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
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);
Top comments (0)