
Spread and Rest - How it works and how to use them
In today’s article we are going to talk about one of the features of the ES6 version(ECMAScript 2015) of JavaScript which is Spread operator as well as Rest operator.
These features may be very useful when developing and working with front-end frameworks. We will explain how you can use it in practice when copy and merge arrays and object literals, insert data, with the benefits of making your code cleaner. Let's get into it and see how it works.
Spread Operator Definition and Syntax
Spread operator looks like three dots (...) and what it does, it takes the group in expression and it tries to “spread” it to multiple values.
You can take the elements of an existing array to a new array. When we have an iterable like for example an array with multiple values, it expands the values in that array into individual elements.
It is mostly used in the variable array where there is more than one value are expected.
var variable = [...value];
There are basically 3 occasions where you can use spread - In array literals, object literals and in function calls.
Also it can be used to make copies of objects, so let's start with that.
Copy of an array or object
Making copies of non-primitives can be tricky. If you copy a primitive, like a number or a string, you will create a real copy, but with objects it's different.
Lets create an array with 3 elements. Then we make a shallow copy of that array using spread operator and the third array is a new array, but basically it's the same array as array1 - that's because when you assign array1 to be array3, the reference to the array1 is copied, not the values themselves.
That’s JavaScript copy by reference. When we make changes to the array1, also array3 will be altered, because the reference of the variable is the same place in memory. With the spread operator, we actually create a new array completely - the values will be copied and JS will handle this new object as a separate, like newly declared entry to the memory. By making changes to the new array2, the original would not be affected. This is called deep copy.
let array1 = ['h', 'e', 'y']; let array2 = [...array1]; let array3 = array1 console.log(array2); //Array(3) [ "h", "e", "y" ] array1.shift(); console.log(array1); //Array [ "e", "y" ] console.log(array2); //not changed - Array(3) [ "h", "e", "y" ] console.log(array3); //Array [ "e", "y" ]
Note: Spread only goes one level deep when copying an arrays or objects, it doesn't perform a deep copy of nested data structures. (for that you would need const myClone = JSON.parse(JSON.stringify(objToClone))
This next example will show creating a copy of an object.
const myName = { firstName :'Tibor', middleName : '', lastName : 'Kopca' } const newName = {...myName} //using spread we copy the object newName.middleName = 'Tiburon' //we add new atribute to copied object console.log("old name was :"+myName.firstName+" "+myName.middleName+" "+myName.lastName) //old name was :Tibor Kopca console.log("new name is :"+newName.firstName+" "+newName.middleName+" "+newName.lastName) //new name is :Tibor Tiburon Kopca
Spread in array literals
Spread operator can be used in many cases, like when we want to expand,copy,concat the first object.
We can effectively merge two or more iterables without the need of the concat method.
Here is some example of the use when you need to add elements from array1 and array2 together, combining them into one.
Note: for the large data using spread operator will result slower compared to the native concat()
method.
let array1 = [1,2,3,4,5,6]; let array2 = [7,8,9]; let arrayConcat = array1.concat(array2); console.log(arrayConcat); //Array(9) [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] let arraySpread = [...array1, ...array2]; //Combining both arrays into one console.log(arraySpread); //Array(9) [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Likewise if we want to expand one array for more values:
let arrayExpanded = [...array2, 10, 11]; console.log(arrayExpanded); //Array(5) [ 7, 8, 9, 10, 11 ]
Or we can concatenate string arguments. Notice the difference in two last rows.
console.log("Hello", "Ma-no.org"); //Hello Ma-no.org //concat() text let text = ["Hello", "world", "!"]; console.log(text); //Array(3) [ "Hello", "world", "!" ] console.log(...text); //Hello world !
Here we can see that strings are also iterable.
console.log(..."VICTORY!"); // V I C T O R Y !
Here is another example of concatenated arrays into one big array.
Order of adding the arrays does matter in this case.
let pets = ['cat', 'dog', 'bird']; let kids = ['Peter', 'Suzan']; const fullfamily = [...kids, ...pets]; console.log(fullfamily); //Array(5) [ "Peter", "Suzan", "cat", "dog", "bird" ]
Spread in Function calls
Let’s look at some examples when working with math object. Here we will for example find the minimum from a comma separated list of arguments - the numbers 0 to 4, and the lowest number is ‘0’. With no arguments it will be infinity, etc.
But with the list of numbers in the array it wont work and will return NaN. Now we can make use of the spread operator, which allows us to pass every number in the array in the Math function as individual arguments.
console.log(Math.min(0,1,2,3,4)); //0 console.log(Math.min()); //infinity let arrayNumbers = [0,1,2,3,4]; console.log(Math.min(arrayNumbers)); //NaN let arrayNumbersWithSpread = [0,1,2,3,4]; console.log(Math.min(...arrayNumbersWithSpread)); //0
Array to arguments
Now this next thing is interesting. When you use spread to access content of an iterable, it will return the content.
Instead of passing every argument, you can pass in an array with all the arguments. The result will be the same as if you would pass all arguments one by one.
const myTeam = ['Ivet', 'Ismael', 'Luigi', 'Silvia'] function printTeam(person1, person2, person3){ return document.write(`Ma-no has the best team with - ${person1}, ${person2}, ${person3}.`) } printTeam(...myTeam)
Object literals and Spread
Just like with arrays, we can use spread to combine and copy objects, it's the same principle.
Let's say we have object holding some name, using spread followed by object name will access the property (content) of the object and log it out like this:
const myName = { firstName :'Tibor', middleName : '', lastName : 'Kopca' } console.log({...myName}) //Object { firstName: "Tibor", middleName: "", lastName: "Kopca" const newName = {...myName} //using spread we copy the object newName.middleName = 'Tiburon' //we add new atribute to copied object console.log("old name was :"+myName.firstName+" "+myName.middleName+" "+myName.lastName) // old name was :Tibor Kopca console.log("new name was :"+newName.firstName+" "+newName.middleName+" "+newName.lastName) // new name was :Tibor Tiburon Kopca
Of course just like by arrays the spread operator can be used to concatenate objects. As spread were shorthand for the Object.assign() method..
const obj1 = {title : 'The Wall'} const obj2 = {author : 'Pink Floyd'} const music = {...obj1, ...obj2} console.log(music) //Object { title: "The Wall", author: "Pink Floyd" }
Destructuring
First start with destructuring, because this leads directly to the rest operator. As the name suggests, destructuring is to take an object or an array and convert it into smaller objects or smaller elements with fewer variables. In the second part we have a function that returns sum and deduction of 2 numbers. We can destructure its return into 2 variables like you see down here.
let letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] const [element1, element2, element3] = letters console.log(element1, element2, element3) //a b c function addAndDeduct(num1, num2){ return [num1 + num2, num1 - num2] //this will return first number in array summed and the second deducted } const [sum, deduct] = addAndDeduct(1,5) console.log(sum) //this will print variable sum which is 6 console.log(deduct) //-4
Rest Operator
Very shortly we will mention the rest operator, which looks exactly like the spread operator. It allows us to represent an indefinite number of arguments as an array - basically collects all remaining arguments of a function into an array. It functions similar to the spread operator, but there is a slight difference. Only the last parameter can be a "rest parameter". Lets see some examples of the use, and notice the result is an array. In this example, the first argument is mapped to a and the second to b, so these named arguments are used as normal. However, the third argument, ‘args’, will be an array that contains the 3rd, 4th, 5th, 6th ... nth — as many arguments that the user includes.
function useRest(a, b, ...args) { console.log(args); } useRest(1,2,3,4,5); //Array(3) [ 3, 4, 5 ]
So the three dots ( ...) represent both the spread operator and the rest parameter. Here are the main differences: The spread operator unpacks elements. The rest parameter packs elements into an array. The rest parameters must be the last argument of a function. However, the spread operator can be anywhere.
function foo(a, ...rest, b) { //will produce error }
Conclusion
In this tutorial we learned about JavaScript spread and rest operators and how to handle them.
Lastly we advise checking for browser compatibility at MDN web docs if you liked this article and you're up to using spread operator in practice.
That's pretty much it, we hope you enjoyed the article.
Image from Pixabay