DEV Community

Cover image for Shallow Copy and Deep Copy
AditiJ
AditiJ

Posted on • Updated on

Shallow Copy and Deep Copy

Table of Contents

  1. Introduction
  2. Shallow Copy
  3. Deep Copy
  4. Summary
  5. References

Introduction

Good day, folks!
Often times we want to create copies of variables for different purposes. There can be two different kinds of copies created - Shallow copy and Deep copy. We commonly encounter shallow copy and deep copy understanding and differences during interviews. So, let's go over the terminologies and related examples.

Shallow Copy

Shallow copy method creates a copy where the source and the copied variable reference remain the same. This means that modifications made in one place would affect both places.

Here's an example to get a better understanding:

const first_person = {
name: "Jack",
age: 24,
}

const second_person = first_person;

second_person.age = 25;

console.log(first_person.age); // output: 25
console.log(second_person.age); // output: 25
Enter fullscreen mode Exit fullscreen mode

Changing the age property value of second_person object, changes the first_person object's age property too.

Now let's look at another example:

const first_person = {
  name: "Jack",
  age: 24
};
let second_person = first_person;
second_person = {
  name: "Jack",
  age: 23
};

console.log(first_person.age); // Output: 24
console.log(second_person.age); // Output: 23
Enter fullscreen mode Exit fullscreen mode

Uh, what happened here? 🤔 Why aren't the values same?
Well, here we are not changing the values of a specific property rather we are assigning a new object.

Deep Copy

Deep copy method creates a copy where the source and the copied variable reference are completely different. This means that modifications made in one place would only affect the variable where we are making a change.

Now let's look at the examples:

If the objects/arrays are not nested, then we can achieve deep copy by using

Spread (...) operator

Using the spread syntax, we can create a deep copy if there is no nesting.

const first_person = {
name: "Jack",
age: 24,
}

const second_person = { ...first_person };

second_person.age = 25;

console.log(first_person.age); // output: 24
console.log(second_person.age); // output: 25
Enter fullscreen mode Exit fullscreen mode

Let's take a look when there is nesting.

const first_person = {
  name: "Jack",
  age: 24,
  address: {
    apartment: "A",
    city: "London"
  }
};

const second_person = { ...first_person };

second_person.age = 25;
second_person.address.apartment = "N";
console.log(first_person.address.apartment); // output: N
console.log(second_person.address.apartment); // output: N
Enter fullscreen mode Exit fullscreen mode

In case of nesting, the spread operator creates a shallow copy.

If the objects/arrays are nested, then we can achieve deep copy by using

JSON.parse() and JSON.stringify()

const first_person = {
  name: "Jack",
  age: 24,
  address: {
    apartment: "A",
    city: "London"
  }
};

const second_person = JSON.parse(JSON.stringify(first_person));

second_person.age = 25;
second_person.address.apartment = "N";
console.log(first_person);
console.log(second_person);
Enter fullscreen mode Exit fullscreen mode

Output:
Output of JSON.parse and JSON.stringify

Combining these two creates a deep copy even when there is nesting involved.

Summary

  • Shallow copy method creates a copy where the source and the copied variable reference remain the same. Changing one, would change the other as well.
  • Deep copy method creates a copy where the source and the copied variable reference are entirely different. Changing one, would not affect the other one.
  • Common methods like Array.concat(), Array.from(), Object.assign(), etc creates a shallow copy.
  • Spread(...) operator, creates a deep copy when there is no nesting.
  • One of the ways to create a deep copy is by using JSON.parse() and JSON.stringify().

References

Oldest comments (15)

Collapse
 
maitrakhatri profile image
Maitra Khatri

Learnt something really important and cool today. Knew the spread operator thing but didn't know the technical name and reason. Thank you for this !! Very simply explained.

Collapse
 
aditi05 profile image
AditiJ

Thank you, Maitra! Glad, it was helpful! :)

Collapse
 
supportic profile image
Supportic • Edited

You probably mix things up here or maybe I understood it wrong.

Common methods like Array.concat(), Array.from(), Object.assign(), etc creates a shallow copy.
Shallow copy method creates a copy where the source and the copied variable reference remain the same.

let count = [2, 4, 5, 6, 7, 8, 9, 13];
let count2 = Array.from(count);
count2[8] = 14;
console.log(count); // [2, 4, 5, 6, 7, 8, 9, 13]
console.log(count2); // [2, 4, 5, 6, 7, 8, 9, 13, 14]
Enter fullscreen mode Exit fullscreen mode

Shallow copies are non-reference copies but only for the first level in case of non-primitive types like object or arrays. JSON.parse(JSON.stringify(obj)) is also not the ultimate solution because it fails for some types which cannot be parsed like NaN or Infinity [ref]
And you list the spread operator under the headline deep copy but it's also a shallow copy.

Collapse
 
lexlohr profile image
Alex Lohr

I believe you misunderstood those terms. First of all, there are 3 concepts, not two:

  • reference copy: only the reference to an object is assigned to another variable
  • shallow copy: the highest level enumerable properties of an object are copied into a new object
  • deep copy: the object properties are recursively copied into a new object
const obj = { x: 1, y: { a: true, b: false } };
const refCopy = obj;
const shallowCopy = { ...obj };
const deepCopy = structuredClone(obj);
// changing the topmost level:
obj.x = 2;
console.log(refCopy.x, shallowCopy.x, deepCopy.x) // 2, 1, 1
obj.y.b = true;
console.log(refCopy.y.b, shallowCopy.y.b, deepCopy.y.b) // true, true, false
Enter fullscreen mode Exit fullscreen mode

Also, you shouldn't use JSON.parse/stringify to deep copy objects. It's neither the safest way nor should it be recommended. You'll lose some data (e.g. Date, Function, RegExp), have a high chance of errors (e.g. using Symbols, recursive references, etc.) and have a rather bad performance penalty compared to structuredClone.

Collapse
 
aditi05 profile image
AditiJ

Thanks for pointing it out! I probably should have just clarified a bit more.
I have edited it and hope it's a little better now.
Thanks again!

Collapse
 
bias profile image
Tobias Nickel

I sometimes used the stringify and parse method too. Underscore and lodash library have deepclone methods.

Once I implementes a kind of ORM. it had query batching. and I wanted to every client to receive a copy if the data. I implemented my own deepclone. It was much faster than underscode. the reason is not that I am so smart, but that my cloneFunction did not need to handle circular objects.

i consider stringify amd parse as quick and dirty, but totally valid option, because you can change the implemebtation down the road, when you get new requirements, such as more performance, more datatypes, or just make it look more professional.😉

Collapse
 
uponthesky profile image
UponTheSky

I guess JS's way of dealing with objects so messy. Why doesn't it have any explicit function like Python's "deepcopy"? Relentlessly nuisances come up and people have to make workarounds like ones mentioned here. Gross...!

Collapse
 
mat3uszkek profile image
Mateusz Skrobiś
Collapse
 
uponthesky profile image
UponTheSky

Oh, I gotta check this out thanks!

Collapse
 
curiousdev profile image
CuriousDev

I think, depending on what you want to achieve, you should create your own copy function. This way you can decide for every reference, if it has to be cloned (new object) or be kept (as a reference).

Collapse
 
shubhamkumar648 profile image
Shubham Kumar

Well Articulate Aditi, Learn a lot

Collapse
 
fuzenco profile image
fuzenco

Thank you. This helps me understand a problem I ran into a while back. As someone who’s not really versed in JS, my situation was frustrating. I should take the time and study JS starting from its foundation. Do you recommend any good books/sites?

Collapse
 
aditi05 profile image
AditiJ

Absolutely! It would be best if you spent some time with JS before moving to any framework. Build small projects, read basic stuff off MDN, and you are good to go!

Collapse
 
mohammadparsajavidi profile image
mohammadparsa-javidi

That was so great 👌

Collapse
 
amulgaurav profile image
Amul Gaurav

This is very insightful. Got surprised after seeing how spread operator can affect in two different ways.

Thank you very much Aditi! Can you point some resources which you've used to write this super informative article.