Deep Cloning Objects In JavaScript (And How It Works)

Chris Chu

If you plan on coding with JavaScript, you need to understand how objects work. They’re one of the most important elements of JavaScript and a deep (pun intended 😉) understanding of objects will always be handy. Especially when cloning an object, it isn’t as simple as it might seem.

You need to clone an object if you don’t want to mutate your original object. For example, if you have a function that takes in an object and alters it, you probably don’t want to mutate your original object.

Check out this other post on Alligator.io on JavaScript objects if you're looking for a quick crash course.

Learn JavaScript basics for free

So let’s create an object in JavaScript:

let testObject = {
  a: 1,
  b: 2,
  c: 3
};

In the above code snippet, we initialize a new object and assign it to the variable testObject. Now for most beginners, they’ll try to create a copy of this object to manipulate in their code by assigning the testObject to a new variable. I’ve personally been guilty of this for longer than I care to admit.

Below is a code snippet that shows why that won’t work.

let testObject = {
  a: 1,
  b: 2,
  c: 3
};

// Creating a new variable that 'copies' our testObject
let testObjectCopy = testObject;

testObject.a = 9;
console.log(testObjectCopy.a);
// This returns a = 9

As shown in the code snippet above, creating the new variable testObjectCopy doesn’t actually create a copy of testObject. Instead, it’s simply referencing the testObject. Any changes you make to the assumed copy will reflect in the original object as well.

Looping through the object and copying each property to a new object wouldn’t work either.

const copyObject = object => {
  // This is the object that will store the original object's properties
  let copiedObj = {};
  
  for (let key in object) {
    // This copies each property from the original object to the copy object
    copiedObj[key] = object[key];
  }

  return copiedObj;
};

const testObject = {
  a: 5,
  b: 6,
  c: {
    d: 4
  }
};

copyObject(testObject);

There are several issues with the approach above:

  • Point 1. A loop that copies each property to a new object would only copy enumerable properties on the object. Enumerable properties are properties that will show up in for loops and Object.keys.
  • Point 2. The copied object has a new Object.prototype method, which is not what you want when you copy an object.
  • Point 3. If your object has a property that is an object, your copied object will actually refer to the original instead of creating an actual copy. This means that if you change that nested object in the copied object, the original gets changed as well.
  • Point 4. Any property descriptors are not copied. If you set things like configurable or writable to false, the property descriptors in the copied object will default to true.

So How Can I Copy an Object the Right Way?

For simple objects that only stores primitive types like numbers and strings, shallow copying methods like the one above will work. However, if your object features references to other nested objects, the actual object won’t be copied. You would only be copying the reference.

For a deep copy, the easiest option is to use reliable external libraries like Lodash.

Using Lodash Clone And Clonedeep

Lodash comes with two different functions that allow you to do shallow copies and deep copies. These are clone and clonedeep. The great thing about Lodash is that you can import each function individually, without requiring the entire library into your project. This can wildly reduce the size of your dependencies.

const clone = require('lodash/clone'); 
const cloneDeep = require('lodash/clonedeep');

// You could also do:
// const clone = require('lodash.clone');
// const cloneDeep = require('lodash.clonedeep');
// Depends on your style :)

Now to use the clone and clonedeep function, here’s some code to try out:

const clone = require('lodash/clone'); 
const cloneDeep = require('lodash/clonedeep');

const externalObject = {
  animal: 'Gator'
};

const originalObject = {
  a: 1,
  b: 'string',
  c: false,
  d: externalObject
};

const shallowClonedObject = clone(originalObject);

externalObject.animal = 'Crocodile';

console.log(originalObject);
console.log(shallowClonedObject);
// The `animal` property in both the originalObject and shallowClonedObject 
// are both changed this way since it's a shallow copy.

const deepClonedObject = clonedeep(originalObject);

externalObject.animal = 'Lizard';

console.log(originalObject);
console.log(deepClonedObject);

// The 'animal' property in the originalObject changes, but for the
// deepClonedObject, it remains as 'Crocodile' since it copied
// the entire object separately instead of copying the reference.

In the above code, we create our object called originalObject, which stores 7 properties with different values in each. The property d references our externalObject which has the property of animal and a value of 'Gator'.

When we do the clone function from Lodash, it creates a shallow copy of the object, which we assign to shallowClonedObject. Assigning the animal property in our externalObject a new value will change both the originalObject and the shallowClonedObject because the shallow clone was only able to copy the reference to the externalObject. It didn’t create a brand new object for itself.

This is where the clonedeep function comes in. If you do the same process above for the deepClonedObject, the originalObject’s d property is the only one to change.

🤓 Try it out and see how this can help your code! 🚀

  Tweet It

🕵 Search Results

🔎 Searching...

Sponsored by #native_company# — Learn More
#native_title# #native_desc#
#native_cta#