DEV Community

Muhammad Rizwan Ashiq
Muhammad Rizwan Ashiq

Posted on

Normal Copy vs Shallow Copy vs Deep Copy

What is Copy?

Copy is a process of creating a new object in memory and storing the same values as the original object. There are three types of copy:

  1. Normal Copy
  2. Shallow Copy
  3. Deep Copy

Let's look at each of them in detail.

Note: For Primitive values normal copy is OK, but it behaves unexpectedly differently in non-primitive type. Why is that? We will see that below. So, keep in mind that we are talking about objects, arrays, but not primitive values.

Normal Copy

A normal copy of an object is a copy that creates a new object in memory and shares the same reference as the original object. So, if we change the value of any property of the copied object, then the original object will also be affected as both the objects are referencing the same values in memory, as you can see in the below example.

const original = { a: 1, b: 2, c: 3 };
const copy = original;

original.a = 10;
console.log(copy.a); // 10

copy.b = 20;
console.log(original.b); // 20
Enter fullscreen mode Exit fullscreen mode

image

So, in short, a normal copy is not a good idea to make a copy of an object. Because, if we change the value of any property of the new object, then the original object will also be affected, as both objects are referencing the same values in memory.

What's the solution? 🤔

We can use shallow copy or deep copy to make a copy of an object depending on the object structure.

Let's look at each of them in detail.

Shallow Copy

It copies the values of the original object to the new object. To make shallow copy, we can use the spread operator ... to copy the values of the original object to the new object. Check the below example:

const original = { a: 1, b: 2, c: 3 }; // Object with 3 properties, no nested objects
const copy = { ...original }; // ... is the spread operator, which copies the values of the original object to the new object

copy.a = 10; // Change the value of the property of the new object to 10
console.log(original.a); // 1 (not affected)
Enter fullscreen mode Exit fullscreen mode

image

See, in the above example, we have changed the value of the a property of the new object, but the value of the a property of the original object is not affected. This is because, in shallow copy, we are only copying the original object's values to the new object, not the reference of the original object. So, if we change the value of any property of the new object, then the original object will not be affected.

But if the object has a nested object (which means have another object against any key), then it will copy the reference of the nested object of the original object, not the values. Check the below example:

const original = {
    a: 1,
    b: 2,
    c: 3,
    d: {
        e: 4,
        f: 5,
    },
}; // Object with 3 properties, 1 nested object with 2 properties

const copy = { ...original };

copy.d.e = 10; // Change the value of the e property of the nested object
console.log(original.d.e); // 10 (affected)
Enter fullscreen mode Exit fullscreen mode

image

So, if the object has a nested object, then shallow copy has the same problem as normal copy. If we change the value of any property of the new object, then the original object will also be affected, as both objects are referencing the same values in memory.

So, what's the solution? We can use deep copy to make a copy of an object.

Deep Copy

Deep Copy creates a new object in memory and stores the same values as the original object. There are multiple ways to create a deep copy of an object or array, but we will look at the most common ways.

JSON.parse(JSON.stringify(obj))

JSON.stringify() method converts a JavaScript object (or array) to a JSON string, and JSON.parse() method parses a JSON string and constructing the JavaScript value or object described by the string.

const original = { a: 1, b: 2, c: 3 }; // Object with 3 properties, no nested objects
const copy = JSON.parse(JSON.stringify(original)); // Deep copy

copy.a = 10; // Change the value of the a property of the new object
console.log(original.a); // 1 => The value of the a property of the original object is not affected
Enter fullscreen mode Exit fullscreen mode

But, it'll copy the values of the original object to the new object (would not share the same reference), and if we change the value of any property of the new object, then the original object will not be affected, and this also applies to nested object.

But this method has a problem:

  1. If the object has a function, then it will not be copied, as you can see in the below example:
   const obj = {
    a: 1,
    b: 2,
    c: 3,
    d: {
        e: 4,
        f: 5,
    },
    g() {
        console.log("Hello");
    },
   }; // Object with 3 properties, 1 nested object with 2 properties, 1 function

   const copy = JSON.parse(JSON.stringify(obj));

   console.log(copy); // { a: 1, b: 2, c: 3, d: { e: 4, f: 5 } }
Enter fullscreen mode Exit fullscreen mode
  1. If the object has a transferable object like Date, then it will be converted to a string while copying.
   const obj = {
    a: 1,
    b: 2,
    c: 3,
    d: {
        e: 4,
        f: 5,
    },
    g: new Date(),
   }; // Object with 3 properties, 1 nested object with 2 properties, 1 Date object

   const copy = JSON.parse(JSON.stringify(obj));

   console.log(copy); // { a: 1, b: 2, c: 3, d: { e: 4, f: 5 }, g: "2021-09-01T06:30:00.000Z" }
Enter fullscreen mode Exit fullscreen mode
  1. If the object has a circular reference, then it will throw an error.
   const obj = {
    a: 1,
    b: 2,
    c: 3,
    d: {
        e: 4,
        f: 5,
    },
   }; // Object with 3 properties, 1 nested object with 2 properties

   obj.d.g = obj; // Circular reference

   const copy = JSON.parse(JSON.stringify(obj));

   // This will error
   console.log(copy); // Uncaught TypeError: Converting circular structure to JSON
Enter fullscreen mode Exit fullscreen mode

So, what's the solution? We can use structuredClone to make a copy of an object.

structuredClone(obj)

It is a deep copy function that can copy any object, including nested objects, arrays, functions, and even transferable objects, such as Date, Set, Map, etc. It is built into the JavaScript runtime and is available in all modern browsers, so don't even need to install any new package.

const person = {
    name: "Rizwan",
    age: 25,
    address: {
        city: "Rahim Yar Khan",
        country: "Pakistan",
    },
    date: new Date(123),
    education: [
        {
            degree: "BSIT",
            university: "KFUEIT",
        },
    ],
};

const copied = structuredClone(calendarEvent);
Enter fullscreen mode Exit fullscreen mode

In the above example, we not only copy the object, but also the nested array, and even the Date object, and all work precisely as expected:

  • copied.education // [{ degree: "BSIT", university: "KFUEIT" }]
  • copied.date // Date: Wed Dec 31 1969 16:00:00
  • person.education === copied.education // false, means both do not share the same reference

That's right, structuredClone can not only do the above but additionally:

  • Clone infinitely nested objects and arrays
  • Clone circular references
  • Clone any transferable objects, such as Date, Set, Map, Error, RegExp, ArrayBuffer, Blob, File, ImageData, and several more

So for example, this madness would even work as expected:

const kitchenSink = {
    set: new Set([1, 3, 3]),
    map: new Map([[1, 2]]),
    regex: /foo/,
    deep: { array: [new File(someBlobData, "file.txt")] },
    error: new Error("Hello!"),
};
kitchenSink.circular = kitchenSink;

// âś… All good, fully and deeply copied!
const clonedSink = structuredClone(kitchenSink);
Enter fullscreen mode Exit fullscreen mode

Note: JSON.parse(JSON.stringify(obj)) is surprisingly faster than structuredClone(obj)

What to use when?

If you want to copy an object that has no nested objects, then you can use shallow copy using the spread operator (...). But if the object has nested objects, then you should use the deep copy.

As in deep copy, we have multiple options, if the object does not have functions, and transferable objects like Date, etc., then JSON.parse(JSON.stringify(obj)) is best, otherwise structuredClone() is the method you can use.

If you want to copy the reference, then the normal copy is the thing.

Conclusion

In this article, we learned about the difference between Normal Copy, Shallow Copy, and Deep Copy. We also learned how to create shallow and deep copies of objects.

Top comments (1)

Collapse
 
suleman_ali profile image
Suleman Ali

Great It is amazing to know. before reading this article I dont deep understanding of Deep shallow and normal copy of object. though only all these work like referencing toward orignal object like in normal copy. but form you article my concept are cleared now. Thanks for sharing this knowledge.