Before we go into what is shallow copy or deep copy. We need to have knowledge about data structures in JavaScript. So in JavaScript there are 2 types of datatypes i.e Primitive Data Types and Structural Data Types or non-primitive
Primitive Data Types:
- undefined
- boolean
- number
- string
- BigInt
- Symbol
Structural or non-primitive data types:
- Object
- Functions
- Class
This prerequisite was essential for knowing shallow copy and deep copy, no let's start😊.
Shallow Copy
When a reference variable is copied into a new reference variable using the assignment operator, a shallow copy of the referenced object is created.
I know that's a pretty heavy defination😅. So let's try to make it simpler... So Shallow copy simply means when we copy the reference and not the value.
let obj1 = {a: "Ankur", b: "Priya"}
let obj2 = obj1; // Shallow Copy
obj2.a = "Priya"
console.log(obj1) // {a: "Priya", b: "Priya"}
Here we have Shallow copied the reference of obj1 into obj2. But you might be wondering that we simply assigned the value using the = operator than why it copied the reference and not value. So the answer is...
When we assign the value using = operator, For primitive data types the value get's copied but for non-primitive or structural data types the reference gets copied.
let obj1 = {a: "Ankur", b: "Priya"}
let obj2 = obj1; // Reference getting copied as it is non-primitive
obj2.a = "Priya"
console.log(obj1) // {a: "Priya", b: "Priya"}
Deep Copy
Unlike the shallow copy, deep copy makes a copy of all the members of the old object, allocates separate memory location for the new object and then assigns the copied members to the new object.
Again it is very complicated😂, no worries let's make it simpler. So in deep copy the value is copied and the entire new memory location is allocated to the variable.
let a = 20;
let b = a; // Value getting copied as it is primitive
b = 30;
console.log(b)//30
But than you might be wondering that how to deep copy structural data types🙄. So there are 3 ways to deep copy strutural data types...
- Using Object.assign method
- Using Spread operator
- Using JSON.parse and JSON.stringify methods
Using Object.assign method
let obj1 = {a: "Ankur", b: "Priya"}
let obj2 = Object.assign({}, obj1); //Deep Copy
obj2.a = "Priya";
console.log(obj1); //{a: 'Ankur', b: 'Priya'}
for reading more about Object.assign: Link
Using Spread operator
let obj1 = {a: "Ankur", b: "Priya"}
let obj2 = {...obj1} //Deep Copy
obj2.a = "Priya";
console.log(obj1); //{a: 'Ankur', b: 'Priya'}
for reading more about Spread operator: Link
But both of these methods are not safe as they only deep copy at top level anything that is nested is shallow copied. Let's understand with example..
let obj1 = {a: "Ankur", b: "Priya", c: {d: "Tanay", e: "Gaurav"}} // c is nested
let obj2 = {...obj1} //Deep Copy At top level
obj2.a = "Priya";// Property a and b is top level so deep copied
obj2.c.d = "Changed"; // Property d is nested inside c so it is shallow copied
console.log(obj1); //{a: 'Ankur', b: 'Priya', c: {d: 'Changed', e: 'Gaurav'}}
Here the properties like a and b are at the top level so they are deep copied but the nested property c is shallow copied. So on changing c it would have reflect both the objects.
Don't worry to overcome this there is a way😊..
Using JSON.parse and JSON.stringify
let obj1 = {a: "Ankur", b: "Priya", c: {d: "Tanay", e: "Gaurav"}} // c is nested
let obj2 = JSON.parse(JSON.stringify(obj1)) //Deep Copy At all levels
obj2.a = "Priya";// Property a and b is top level
obj2.c.d = "Changed"; // Property c is nested inside c so it is shallow copied
console.log(obj1); //{a: 'Ankur', b: 'Priya', c: {d: 'Tanay', e: 'Gaurav'}}
Here JSON.stringify converts the object contents into string and then JSON.parse parses it to create a new object. for reading more about JSON.parse: Link
for reading more about JSON.stringify: Link
So you might be thinking so this would be the go to method for deep copying the objects. But sadly that is not true😂😅. Let's see why Consider this code
const obj = {a : "Ankur", b: 2, c: {d: 3, e: function(){
console.log("Inside e")
}}}
const obj1 = JSON.parse(JSON.stringify(obj));
obj.c.d = "Tanay";
console.log(typeof obj1.c.e) // undefined ????
See here we are getting undefined why??. Because JSON.stringify method converts the JS object into JSON string, so it also tries to convert the function e which then becomes undefined.
So Remember this method of deep copying would not work if your object has one of these things Dates, functions, undefined, Infinity, RegExpression, Maps, Sets, blobs, Filelists, Imagedatas and many more complex data types...
So what's the full proof solution???
- To use libraries like loadash, Randa etc.
- To implement your own utility function.
Using loadash:
var loadash = require("./loadash");
const obj = {a : "Ankur", b: 2, c: {d: 3, e: function(){
console.log("Inside e")
}}}
const obj1 = loadash.deepclone(obj);
obj.c.d = "Tanay";
console.log(typeof obj1.c.e) // the entire function
Using Utility Function
const deepCopy = (obj) => {
// if it is not object
if (typeof obj !== "object" || obj == null) return obj;
//Checking if we have to create a object or array
const newObject = typeof obj === "object" ? {} : [];
Object.keys(obj).map((key) => {
const value = obj[key];
newObject[key] = deepCopy(value); //Recursive Call
});
return newObject;
};
const obj1 = deepCopy(obj);
console.log(obj1);
Hope you learnt something from the blog, please give me some feedbacks in it😊😊