New operator ... was introduced in ES6 . Depending on the context it is called the Spread or the Rest operator. This operator provides a cleaner syntax when you are dealing with arrays or functions with a variable number of arguments and it benefits functional programming paradigm. Let’s explore it in more details.
Spread Operator
The spread operator allows to split elements of an array to separate values, that can be used as a function arguments or inside an another array. Let’s take a look at the following example:
- Let’s create two arrays. One contains a list of dynamic programming languages. The other contains a list of static programming languages.
1 2 3 4 5 6 7 8 9 10 |
const staticLanguages = [ "C", "C++", "Java" ]; const dynamicLanguages = [ "JavaScript", "Ruby", "PHP" ]; |
2. Now we want to create a list of all languages, using arrays that we have just defined plus adding some more languages to it. And then we’ll print the array and its length on the console .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
const allLanguages = [ staticLanguages, "C#", dynamicLanguages, "Python" ]; console.log(allLanguages); // as a result we get a two level array - not exactly what we wanted // [ // ["C", "C++", "Java"], // "C#", // ["JavaScript", "Ruby", "PHP"], // "Python" // ] console.log(allLanguages.length); // 4 |
As seen instead of one level list of values we’ve got two level array. The array allLanguages has 4 elements, two of them are arrays, and two of them are strings. Not exactly what we wanted. To get a one level array we can use ... operator:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
const allLanguages = [ ...staticLanguages, "C#", ...dynamicLanguages, "Python" ]; console.log(allLanguages); // this time we get right one level list as a result // [ // "C", // "C++", // "Java", // "C#", // "JavaScript", // "Ruby", // "PHP", // "Python" // ] console.log(allLanguages.length); // 8 |
As you can see the ... operator expands (spreads) the arrays elements. Now, the array allLanguages has 8 elements, all of them are strings.
To get the same result in ES5, we would use the concat() method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
const allLanguages = [].concat( staticLanguages, ["C#"], dynamicLanguages, ["Python"] ); console.log(allLanguages); // [ // "C", // "C++", // "Java", // "C#", // "JavaScript", // "Ruby", // "PHP", // "Python" // ] console.log(allLanguages.length); // 8 |
As you see the solution is not that elegant. Using the ... operator provides better syntax.
One more example. We have the sum function, that expects three arguments and logs on the screen the sum of the arguments.
1 2 3 4 5 |
function sum (a, b, c) { console.log(a + b + c); }; const numbers = [2,4,10]; sum(numbers); //2,4,10undefinedundefined |
Without the ... operator the function receives only one argument – a . Its value is the array we sent. b and c don’t receive any values so they stay undefined. As a result of a + b + c we have:
1 |
[2,4,10] + undefined + undefined = 2,4,10undefinedundefined |
And again, to fix it we can use the ... operator. It spreads the elements of the array so we get the correct result:
1 2 3 4 5 |
function sum (a, b, c) { console.log(a + b + c); }; const numbers = [2,4,10]; sum(...numbers); //16 |
The ... operator can be used with the push() method:
1 2 3 |
a = [4,5,6]; b = [1,2,3]; b.push(...a); // b = [1,2,3,4,5,6] |
The ... operator provides a simple way to clone and concatenate arrays.
Concatenate arrays:
1 2 3 4 |
const odds = [1, 5, 7]; const evens = [4, 6, 8]; const all = [...odds, ...evens]; console.log(all); // => [1, 5, 7, 4, 6, 8] |
Clone arrays:
1 2 3 4 |
const numbers = [1, 2, 3, 4, 5]; const clone = [...numbers]; console.log(clone); // => [1, 2, 3, 4, 5] console.log(clone === numbers); // => false |
The ... operator can be used only in places where multiple values expected: inside arrays, arguments of a function, etc. In other places it will cause a syntax error:
1 2 3 4 |
const a = [1,2,3]; const b = ...a; //will throw an error //it's the same as we would do something like this: const b = 1, 2, 3; // which does not make sense, only one value is expected here |
You can use the ... with simple arrays only. It can’t be used with objects.
Rest Operator
If the last argument of a function has the ... operator as a prefix, it’s called the rest operator. It allows to use a variable number of arguments as an array.
You can think of the rest operator as a successor of the arguments object. Let’s take a look at how the arguments object works:
1 2 3 4 |
function logArguments () { console.log(arguments); } logArguments(1,2,3,4,5); // [1,2,3,4,5] |
The arguments object looks like an array but it’s an array like object. Array methods are not applicable to it.
1 2 3 4 |
function logArguments () { arguments.filter(arg => arg % 2 === 0); // This throws an exception } logArguments(1,2,3,4,5); |
For applying array methods to arguments object you can use the call() method:
1 2 3 |
Array.prototype.filter.call(arguments, arg => arg % 2 === 0); //or [].filter.call(arguments, arg => arg % 2 === 0); |
This syntax introduces a couple problems:
- We have to indicate the context of the function invocation manually
- We have a list of all the arguments passed to a function , even if we have named arguments in the function.
- It has the hard-coded name. We cannot use a custom name.
- Shorter syntax is more preferable
The ... operator provides a cleaner and more intuitive syntax, custom naming, a list of arguments as an array (not object), and it contains only arguments without a name in a function.
1 2 3 4 5 6 |
function logArguments (a, b, ...c) { console.log(a); // 1 console.log(b); // 2 console.log(c); // [3,4,5] }; logArguments(1,2,3,4,5); |
In this example we pass numbers as parameters to the logArguments function. It has regular arguments a and b . They receive values 1 and 2 . The last argument c with ... as a prefix collects all the rest values as an array.
Let’s take a look at one more example:
1 2 3 4 5 6 |
function logArguments (a, b, ...c) { console.log(c); }; logArguments(1,2,3,4,5); // [3,4,5] logArguments(1,2,3); // [3] Creates an array with only one element logArguments(1,2); // [] creates an empty array, as there is no arguments left |
Argument with the ... operator as a prefix is a simple array, so you can apply array methods to it.
1 2 3 4 5 |
function logArguments (...numbers) { const evenNumbers = numbers.filter(arg => arg % 2 === 0); //works fine console.log(evenNumbers); } logArguments(1,2,3,4,5); // shows [2,4] |
You can use the ... operator with the last parameter only. Otherwise it will throw an exception:
1 2 3 |
function logArguments (...numbers, a, b) { // This throws an exception console.log(numbers); } |
Conclusion
The ... operator looks simple but it’s very powerful. It ads new functionality and improves syntax significantly. It has two meanings: spread and rest.
The Rest operator provides a simple and clean way to collect arguments as an array. It’s a better alternative for the arguments object.
The Spread operator improves syntax when you are dealing with simple arrays, simplifies concatenation and cloning arrays. You can use this operator when you create a new array out of existing arrays.
Pingback: http://vioglichfu.7m.pl/()