DEV Community

Cover image for Understanding the JavaScript Spread Operator - From Beginner to Expert
Nya
Nya

Posted on • Updated on

Understanding the JavaScript Spread Operator - From Beginner to Expert

Introduction

The spread operator '…' was first introduced in ES6. It quickly became one of the most popular features. So much so, that despite the fact that it only worked on Arrays, a proposal was made to extend its functionalities to Objects. This feature was finally introduced in ES9.

The goal of this tutorial, which is divided into two parts, is to show you why the spread operator should be used, how it works, and deep dive into its uses, from the most basic to the most advanced. 

Here is a short summary of the contents of this tutorial:

What we are going to learn

Part 1

  1. Why the spread operator should be used
  2. Cloning Arrays/Objects
  3. Converting Array-like structures to Array
  4. The spread operator as an argument
  5. Adding elements to Arrays/Objects
  6. Merging Arrays/Objects

Part 2

  1. Destructuring nested elements
  2. Adding conditional properties
  3. Short circuiting
  4. The rest parameter (…)
  5. Default destructuring values
  6. Default properties

Why you should use the spread operator

After reading the previous list, you may be thinking something along these lines: "But JavaScript has functions to do all those things… Why would I use the spread operator?" Allow me to introduce you to immutability:

From Oxford Lexico: Immutability -  unchanging over time or unable to be changed.

In software development, we use the term immutable to refer to values whose state cannot change over time. In fact, most of the values that we normally use (Primitive values, such as Strings , Integers etc.) are immutable. 

However, JavaScript has a peculiar behavior when it comes to Arrays and Objects; they are, in fact, mutable. This can become a big problem. Here's an example, illustrating why:

As you can see in the previous code fragment, we have a Squirtle. Our Squirtle has a hp of 100, since we just visited the Pokemon Center. 

Since we want another Squirtle, we declare the variable anotherSquirtle, assigning our original Squirtle as its value. After an arduous battle, anotherSquirtle is defeated. We therefore access anotherSquirtle's hp, and change it to 0. The next step is to check on our original Squirtle. We console.log and…

Wait, what? Our original Squirtle's hp is down to 0! How can this be? What happened to our poor Squirtle? JavaScript mutation happened. Let me explain what's going on. 

When we created the anotherSquirtle variable, and assigned our original Squirtle as its value, what we really did was assign a reference to the memory location of the original Squirtle Object. This is because JavaScript Arrays and Objects are reference data types. Unlike Primitive data types, they point to the memory address where the actual Object/Array is stored.

To make it easier to understand, you can imagine reference data types as pointers to a global variable. By changing a reference data type's value, what we are really doing is changing the value of the global variable. 

This means that, when we changed the anotherSquirtle's hp value to 0, we were really changing the hp value of the Squirtle Object stored in memory to 0. This is why mySquirtle's hp value is 0, because mySquirtle holds a reference to the Object stored in memory, which we changed via the anotherSquirtle variable. Thank you JavaScript.

How do we solve this problem?

To avoid the accidental mutation of variables, what we have to do is create a new instance of our Array/Object whenever we want to copy an Array/Object. How do we achieve this?
 
With the spread operator!! :)

How does the spread operator work?

From the MDN docs: Spread syntax allows an iterable such as an array expression or string to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected, or an object expression to be expanded in places where zero or more key-value pairs (for object literals) are expected
.
To put it simply, the spread operator '…' spreads the items that are contained in an iterable (an iterable is anything that can be looped over, like Strings, Arrays, Sets…) inside a receiver (A receiver is something that receives the spread values). Here are several simple examples with Arrays that will allow you to understand it better:

As you can see, when we use the spread operator on an Array, we obtain each individual item contained in the Array. In all the previous cases, the receiver was a function, the console.log function. Easy enough, right?

Cloning Arrays and Objects

Now that we now how the spread operator works, we can make use of it to copy Arrays and Objects immutably. How? By spreading the contents, and then using either the Array or Object literals ([] and {} respectively) to generate a new instance of the Array/Object. Let's take the previous Squirtle example, and fix it, by cloning the *mySquirtle * variable immutably:

By destructuring the contents of the mySquirtle variable with the spread operator, and using the Object literal, we are creating a new instance of the Squirtle Object. This way, we prevent accidental variable mutation

To copy an Array, we use exactly the same syntax:

Note: Bear in mind the fact that the spread operator only performs shallow copies. This means that if you have a reference data type stored inside your Array/Object, when you make a copy with the spread operator, the nested Array/Object will contain a reference to the original, and will thus be mutable.

Converting Array-like Objects to Arrays

Array-like Objects are very similar to Arrays. They usually have numbered elements and a length property. However, they have one crucial difference: Array-like Objects do not have any of the Array functions.

Among the Array-like Objects are the HTML node lists returned by most DOM methods, the arguments variable generated automatically in every JS function and a few others.

With the same syntax as when cloning arrays, we can use the spread operator to transform Array-like structures to Array, as an alternative to using Array.from. Here's an example, converting a NodeList to an Array: 

With this technique, we can transform any Array-like structure to Array, and thus have access to all the Array functions.

The spread operator as an argument

Some functions accept a variable number of parameters. A great example of these types of functions are the ones in the Math collection. For our example, let's pick the Math.max() function, which accepts n numeric parameters, and returns the largest one. Imagine we have an Array of numbers, which we want to pass to the Math.max() function. How do we do it? 

We could do something like this (don't hate me for the following code):

But, of course, doing this would be suicide. What if we had 20 values? Or 1000? Are we really going to access each value by index? The answer is no. As we already know, the spread operator takes an Array and extracts each individual value. This is just what we're looking for! Therefore, we can do this:

Spread operator to the rescue!

Adding new elements 

Adding items to an Array

To add new elements to an array, we first spread the Array's contents, and use the Array literal [] to create a new instance of the Array, containing the original array's contents, plus the values we want to add :

As you can see, we can add as many new items as we want. 

Adding properties to an Object

By using the same syntax as with Arrays, we can easily add new properties when cloning an Object. To switch it up a little, here's a different syntax to add properties to an Object (it can also be used with Arrays):

As you can see, we can declare and initialize new variables directly inside the Object literal, instead of doing so outside. 

Merging Arrays/Objects

Arrays

We can merge two arrays, by spreading them and using the Array literal, like in the previous examples. However, instead of simply adding a new element, we're going to add another (spread) array:

It also works if we have an Array of Objects:

Objects

We can merge two (or more) Objects into a single Object, by using the same syntax as before (you may have noticed by now, that the spread operator is used in a very similar same way, both for Arrays and Objects):

Conclusion

In this first part of the tutorial we have learnt why we should use the spread operator (immutability!), how it works, and several basic uses of said operator. In the second part of the tutorial we will deepen our knowledge this operator with several advanced techniques and use cases. Here's the link to the second part.

Thank you very much for reading :) If you have any doubts or comments don't hesitate to reach out to me, here's a link to my Twitter page.

Top comments (8)

Collapse
 
vlom31 profile image
vlom31

Thank you for this very clear tutorial, it really helped me finally understand these "..." 🙏

Collapse
 
nyagarcia profile image
Nya

I'm so glad it helped! :D

Collapse
 
valxntine profile image
valentine

I'd seen the spread operator in a colleagues code (we aren't directly developers) and I had wondered what it was. Great explanation!

Collapse
 
nyagarcia profile image
Nya

Thank you :D

Collapse
 
abbiranjan profile image
Alok Ranjan

Thanks a lot for your very informative article.
Please keep enlighten us with such nice article.

Collapse
 
nyagarcia profile image
Nya

Thank you for your comment :D

Collapse
 
equiman profile image
Camilo Martinez

Thanks... I see you have a part 2. It will be great if config those articles as a series.

How?
Edit both articles and add the same value on series name 😉.

Collapse
 
nyagarcia profile image
Nya

Just did! Thank you for reading, and for your tip ^