Understanding the JavaScript Spread Operator — From Beginner to Expert

Image for post
Image for post

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 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 to deep dive into its uses, from the most basic to the most advanced.

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

  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
  1. Advanced uses of the spread operator

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 an HP of 100, since we just visited the Pokémon 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? A 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 mySquirtles 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.

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 0 or more arguments (for function calls) or elements (for array literals) are expected or an object expression to be expanded in places where 0 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, etc.) 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:

Three examples of the spread operator used on arrays

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 know 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:

Copying an object immutably with the spread operator

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:

Copying an array immutably with the spread operator

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 argument variables 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 arrays, as an alternative to using Array.from. Here’s an example, converting a nodeList to an array:

Converting a nodeList to Array

With this technique, we can transform any array-like structure to an 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 that 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 1,000? 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

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:

Adding items to an array with the spread operator

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

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

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:

Merging two arrays with the spread operator

It also works if we have an array of objects:

Merging two arrays of objects with the spread operator

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):

Merging two objects with the spread operator

Conclusion

In this first part of the tutorial we have learned 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 of this operator with several advanced techniques and use cases. Here is the link to the second part of the tutorial:

Understanding the JavaScript Spread Operator — Advanced Uses

Thank you very much for reading.

Better Programming

Advice for programmers.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store