Audio tape on a glass table

Deepcopy of JavaScript Objects and Arrays using lodash’s cloneDeep method

While developing JavaScript applications, often we need to make copies of Objects or Arrays containing Objects. Then we want to work with those copies without affecting the original objects.
In a common scenario, we are going to deal with a simple, one level Object (an Object without other Objects nested inside), like in the following example:
let originalObject = {name: 'Bunt', type: 'dog'};

Then, we want to make a copy of the originalObject. In ES6, we are going to do something like this:

let copiedObject = Object.assign({}, originalObject);

This will create the copiedObject with all the properties and respective values of the originalObject. Now, if we want to change a value in the copiedObject, this will not change also the value into the originalObject:

copiedObject.type = 'cat';

console.log(originalObject.type); // will print 'dog'
console.log(copiedObject.type); // will print 'cat'

Now, what is going to happen if we have a nested Object inside our Object, doing the same operations?

let originalObject = {name: 'Bunt', details: {type: 'dog', color: 'brown'}};

let copiedObject = Object.assign({}, originalObject);

copiedObject.details.type = 'cat'; 

console.log(originalObject.details.type); // will print 'cat' 
console.log(copiedObject.details.type); // will print 'cat'

As you can see, when you change the value of a property in the nested Object of copiedObject, that change is perpetuated to the same property of the nested Object in originalObject. You’ll ask why that happened? Because JavaScript passes the primitive values (string, number, etc.) as value-copy and the compound values (Objects, Arrays, Functions) as reference-copy to the value of the primitives on that Object. This way, when we copied the Object containing the nested Object, we have created a shallow copy of that object, meaning that the primitives found at the first level of the Object have values that are copied, thus when we change them into the copiedObject, they are not going to change in the originalObject while all other property found in a nested object will have the value passed as a reference to the same property value into the nested Object of originalObject, thus when we change its value, we actually change the value of the reference, resulting on both nested Objects having the modified property value.

Now, what’s the solution to deep copy an Object or Array, meaning that we want to have a copy free of references at all nested levels so we can change the values inside without the fear of breaking the original?

The first solution you are going to find is to serialize the Object to a JSON, then parse back that JSON to a JavaScript Object, practically passing the Object thru a string that will clean up the Object tree from the references nested inside by recreating a new Object with copied properties and values from the original one.

let copiedObject = JSON.parse(JSON.stringify(originalObject));

But this is a controversial solution. Why? Because this is going to work fine as long as your Objects and the nested Objects only contains primitives, but if you have objects containing functions, this won’t work. Neither for Date.

So, what’s the working solution? We are going to use lodash’s cloneDeep method to deep copy the Object. Lodash is an excellent JavaScript utility library for those not knowing it yet. The cloneDeep method will iterate all levels of the original Object and recursively copying all properties found.

The example I will give here is in Angular. Since we don’t want to carry all the bundle of lodash to the client, we are going only to import the cloneDeep method.

First, let’s install lodashinto our project:

yarn add lodash or npm install lodash

Once installed, we are going to use in our component:

import * as cloneDeep from 'lodash/cloneDeep';

...

clonedObject = cloneDeep(originalObject);

Importing cloneDeep will add to your build 18kb, a fair price to pay in order to have a reliable deep copy solution for you Objects.

I added a demo app on Github here.

Interested in Angular? Join our Angular Montreal Meetup.

Leave a Reply

Your email address will not be published. Required fields are marked *