Javascript Clone Object | Deep copy object Javascript
Javascript Clone Object | Deep copy object Javascript

In this post you will learn how how to copy objects inside Javascript and how to implement deep copy (or deep clone) method on your own.

The first question here that you for sure want to ask is "Why do we need a copy of our object?".

And typically people write something like this

const a = {b: 1}
const newA = a
newA.c = 2
console.log(a, newA)

The idea is that we copy an a to newA and then modify our newA. This code won't work and both object are changed.

It happens because objects are assigned by reference and not by value. Both this objects are referencing the same data in memory.

The typical solution to this problem is to use Object.assign or spread.

const a = {b: 1}
const newA = {...a, c: 2}
console.log(a, newA)

This will work fine and we will change only newA now. With using of spread operator we created a completely new object and now it is safe and correct.

In the same way you can use Object.assign.

const a = {b: 1}
const newA = Object.assign({}, a, {c: 2})
console.log(a, newA)

It is an older approach but it works just as well.

Deep copy

Now we must talk about deep copy inside Javascript. It means that we want to copy a nested object.

const a = {b: {c: 2}}
const newA = {...a, d:2}
newA.b.f = 3

We used here spread but actually we will get f=3 in both objects simultaneously.

Spread it not working correctly with nested objects because nested properties will be referenced again

This is why we must find another approach.

Json stringify

And the easiest approach might sound a little bit hacky.

const a = {b: {c: 2}}
const newA = JSON.parse(JSON.stringify(a))
newA.b.f = 3

Here we used stringify and parse which generates a string from object which we parse then back to JSON. It will essentially create a new nested object.

And here you might ask "It looks like awesome idea. Why we don't use it everywhere?". The main problem is that if you have a huge object or you have not normal object or array then this method will break. For example if you have functions or DOM elements inside then it won't work.

It is a good solution for simple constructions but it is much better to have a specific method that does deep copy.

Using a library

The best possible variant for deep copy is using a library. Let's put in our index.html a script for lodash library.

<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>

If you don't now what is Lodash it's a super popular library for data transformations. You have hundred of different functions inside. And one of this functions is called cloneDeep. This is why here instead of JSON.stringify we can use a library.

const a = {b: {c: 2}}
const newA = _.cloneDeep(a)
newA.b.f = 3

As you can see in browser it works exactly like before but we used a library and this code won't break with big amount of data.

If we are talking about production project I highly recommend you to use this function and you are good to go.

But you must use it only when you have nested objects or arrays. It is much slower than using spread to copy not nested data. This is why I don't recommend you to put it in every single case.

Custom deep copy implementation

But obviously to understand how deep copy function works we must implement it on our own. Here I want to write a function which will implement a deep copy for arrays and objects.

const clone = (input) => {
  if (input === null || typeof input !== "object") {
    return input;
  }
};

Here we created clone function where we can throw any data. And we check inside that if it is not array or object we simply return new value. It is totally fine as we get new value.

const clone = (input) => {
  if (input === null || typeof input !== "object") {
    return input;
  }

  const initialOutput = Array.isArray(input) ? [] : {};

  return Object.keys(input).reduce((acc, key) => {
    acc[key] = clone(input[key]);
    return acc;
  }, initialOutput);
};

Here we created initialOutput for either array or object depending what we passed inside. After this we used reduce function on Object.keys which gives us keys of the object or indexes of the array. Then we go through every single key and call clone function recursively. Which actually means it doesn't matter how deep our nesting is, we will copy every single property.

const a = {b: {c: 2}}
const newA = clone(a)
newA.b.f = 3

As you can see in browser it works exactly like a method from Lodash. And if you are interested to learn why comparison is bad in Javascript make sure to check this post also.

📚 Source code of what we've done