Deep Vs Shallow Copy in JavaScript
A full comparison between deep and shallow copy to understand more the difference and when to use both of them.

A copy means that you create a new identical element with its properties from the original and you expect that original element stays the same when you change in this copy, but you if you make a copy which changes in the original!
Then you need to take a look at copying types that we will go through in this article.
In the beginning, we need to understand the concept of pass by value, and pass by reference will help us to understand shallow and deep copy.
Pass by value and by reference concept
There are two ways to pass or copy things, one by value, and the other is by reference. When you copy something by value means you are creating a new separate and independent value similar to the original ( deep copy ).
But when you copy something by reference you are creating just an alias to the original, not a new or independent copy ( shallow copy ).
And this will take us to know what is the deep and shallow copy!
Deep Copy
When you create a deep copy, you create an identical copy of the original element with its properties.
The original and the copy are not connected, which means if you changed in original properties will not affect or change anything in the copied element.
Shallow Copy
On the opposite side, when you create a shallow copy, you create a new copy that is connected to the original.
So when you change the original element it will also affect and change the copied one and the same if you changed in the copied element it will change in the original.

As shown in the above image that shallow copy still connected, but deep copied elements have different references and not connected at all.
How to create a deep copy
There are many ways to copy elements but which one is a deep copy and which is a shallow copy!
1.Primitive types
All elements with primitive types like numbers, strings, and boolean are deep copied which means that the copied element and the original are not connected and any change won’t affect the original element.
const original = 'Marina';
let copy = original ; //Marina
copy = 'Magdy';
console.log(original); //Marina
console.log(copy); //Magdy
On the other side objects, and arrays are stored just once at the time you create them, and assigning a variable creates a pointer (reference) to that value, so changing in the copy will change in the original object or array too.
2.Objects
Assigning the original object to a new variable creates a shallow copy which means that the original and the copy are connected and any change will affect in them both like the following example :
const car1 = {color : "red" , wheels : 4};
let car2 = car1;
car2.color = "black";
console.log(car1.color);//black
console.log(car2.color);//black
So How to create a deep copy for objects!
2.1 Spread operator
It also called Destructuring which helps to spreads all properties or elements from an array or object into a new element, this creates a deep copy which means when changing in the copy will not affect the original
const car1 = {color : "red" , wheels : 4};
let car2 = {...car1};
car2.color = "black";
console.log(car1.color);//red
console.log(car2.color);//black
2.2 Object.assign
Another way to deep copy object using object.assign() which create a totally new and separate copy.
const car1 = {color : "red" , wheels : 4};
let car2 = Object.assign({}, car1);
car2.color = "black";
console.log(car1.color);//red
console.log(car2.color);//black
2.3 JSON.parse() & JSON.stringify()
This is maybe the most common way to make a deep copy by using JSON.stringify()
which converts passed element into a string. Then, the JSON.parse()
converts it back to the original form or data type and this allows deep cloning for nested objects too which not happening with object.assign or spread operator
const car1 = {color : "red" , wheels : 4};
let car2 = JSON.parse(JSON.stringify(car1));
car2.color = "black";
console.log(car1.color);//red
console.log(car2.color);//black
3. Arrays
Arrays are also just objects, so they have the same problem with a shallow copy, then how can we deep copy arrays too!
3.1 Spread operator
Spread operators are used with arrays too so we can avoid shallow copy to create a new copy which not connected to original
const carsOriginal = ["Audi" , "BMW" , "Toyota"];
let carsCopy = [...carsOriginal];
carsCopy[2] = "Kia"
console.log(carsCopy[2]);//Kia
console.log(carsOriginal[2]);//Toyota
3.2 map, filter, reduce
These methods are introduced by ES6 which helps to create a deep copy of an array by returning a new array not connected to the original, and this also happens with .slice() which returns a new array and helps to create a deep copy.
const carsOriginal = ["Audi" , "BMW" , "Toyota"];
let carsCopy = carsOriginal.map(car => car);
carsCopy[2] = "Kia"
console.log(carsCopy[2]);//Kia
console.log(carsOriginal[2]);//Toyota
3.3 JSON.parse() & JSON.stringify()
Same as objects , it also used with arrays to create a deep copy arrays.
const carsOriginal = ["Audi" , "BMW" , "Toyota"];
let carsCopy = JSON.parse(JSON.stringify(carsOriginal));
carsCopy[2] = "Kia"
console.log(carsCopy[2]);//Kia
console.log(carsOriginal[2]);//Toyota
Conclusion
While creating a copy, You need to make sure if you want to update the original one or not.
If you need it to update the original element you can use shallow copy that follows pass by reference concept, if not then you can use deep copy that follows pass by value concept.
In this article we went through:
- Pass by value and Pass by reference
- Deep vs shallow copy
- How to create a deep copy
Thanks a lot for your time to read this article and if you have any questions please leave a comment.
Happy learning!