A Tale of Two Copies

Ashwin Sathian
Level Up Coding
Published in
4 min readJul 24, 2020

--

Let’s face it — Javascript is a funny language, to say the least. And the internet is filled with articles articulating this. But it’s simple, powerful and ubiquitous, and learning a trick or two wouldn’t go amiss. In this article, we’ll be discussing a tricky bit in Javascript — copying.

Consider two variables a and b. You have a value stored in a, which you wish to copy into b. This can be done as follows

Now you might be wondering what was the point of showing that, for that was obvious. Bear with me. This method holds true so long as a and b are of primitive data types — string, number, boolean, etc. The above bit of code will copy the value stored in a to b and then, changes in one won’t affect the other. Again, obvious.

Let’s take this one step ahead now. What if a was say, an array? “Yea, what about it?”, I hear you ask. Logic dictates that this should not make any difference. The array in a can be copied into b in exactly the same way and changes toa won’t affect b and vice versa.

Surprised? I’m sure that at least a couple of you reading this would certainly be. Feel free to try it out and see it for yourself.

So, what happened there?

This behaviour is a result of the way Javascript works. In JS, Arrays, Objects and Functions(let’s collectively call them Objects, because that’s how JS sees all 3 of them), are treated differently from String, Number, Boolean, Null and Undefined(the Primitives). While the Primitives are passed by value, Objects are passed by reference.

This essentially means that when we assign a Primitive variable to another Primitive variable, like in our simple copy case, the value stored in one variable is copied into the other and after this copying, they are independent of each other. On the other hand, in case of Objects, when you assign an Object variable to another Object variable, both variables are essentially referring to the same Object in memory — just as how Christiano Ronaldo and CR7 refer to the same individual.

It’s because of this pass by reference nature of objects that we saw the arrays from the above code snippet behave the way did. When we work on a or b, we are effectively working on the same array, referred by using different names.

It’s also worth noting that JS is not exclusive in this behaviour — Typescript, which is essentially a superset of JS, shows the same behaviour. So does Python.

What’s the solution?

Time and again, situations might arise when you need to copy an Object-type variable and not end up altering the source variable while working on the copy and vice versa.

Enter Spread …

Introduced in ECMAScript 6(ES6), the spread operator(…) solves this very problem. Using the spread syntax, you can extract the value of an Object, which can subsequently be copied into another variable.

As can be seen from the above code snippet, once we use the spread syntax to copy Objects, changes in one variable don’t affect the value stored in the other and vice versa. What has essentially happened here is that by extracting and copying the value of a to b, we ensured that both a and b are not referring to the same Object in memory.

Yay! We’re sorted — not so fast

As much as spread operator is a lifesaver as far as copying Objects go, there is a catch and a big one at that. Consider the following piece of code

Looks like we still have problems after all.

As you can see, despite using the spread operator to copy the value of a to b, changes to johnMarks affected the marks field for both a and b. This happens because the copying done by means of the spread operator is what is known as a ‘shallow copy’.

Shallow Copy is one where while copying an Object, we copy values of the main Object but pass the reference of Objects within it to the copy. Thus, in our case, both a and b store a reference to the same johnMarks Object. What we need in such cases, is a ‘deep copy’.

In a deep copy, values of the Object, as well as those of Objects within it are copied out, meaning that if we deep copy a to b, the marks field will hold the value of johnMarks and not a reference to it.

How to perform a deep copy?

While various ways exist to perform a deep copy in Javascript, including creating a custom function that could maybe recursively do a shallow copy to emulate a deep copy, I usually go with a simple yet effective method. All we need are two methods already available to us out of the box.

And there you have it — a neat deep copy performed by combining the JSON.parse() and JSON.stringify() methods. What is happening here is that first a string is constructed out of the source Object, which is then used to create a new Object that gets stored in the second variable.

Note that while using this method for deep copying, ensure that the objects involved conform to JSON conventions, to avoid unexpected errors. If they do not, you may have to look at other methods such as perhaps a custom function, as already mentioned.

That’s it then. In this article, we looked at the need for copying in Javascript, two different copying styles and a method each for implementing the copying styles. The methods illustrated and/or mentioned are by no means an exhaustive list, but simply methods I personally favour.

Interested in having a 1:1 chat with me over this story, or Angular, Typescript and Javascript in general? Head over to Hire The Author and let’s connect!

--

--

Full Stack Developer. Find me on Hire The Author, if you have queries about Angular, Node.js, MongoDB, the MEAN Stack or Web Development in general.