Introduction
Objects are a bit weird when it comes to cloning them. Objects when cloned may create a shallow copy or a deep copy depending on how it was cloned. In this blog, we will see the problem that a shallow copy creates and how we overcome it with deep copy, we will also learn different ways to create a deep copy of an object.
Shallow Copy 🐍
Let's say we have an object prakashDetails
with keys state and country having some value, and now we want to create another object ashishDetails
with a different state but the same country. So we can create a copy of the existing object prakashDetails
and assign it to the new object ashishDetails
and then change the state of the newly created object.
Let's see this with an example👇
const prakashDetails = {
state: "Maharashtra",
country: "India"
}
// creating another object ashishDetails by using the above object
const ashishDetails = prakashDetails;
// Changing state of object ashishDetails
ashishDetails.state = "Haryana";
console.log(ashishDetails)
// Output: { state: "Haryana" , country: "India" }
Woaahh!!, we have created a new object by simply using an existing object, amazing right!!. But, there is a small problem, that is, if we check the properties of object prakashDetails
it's value of state has also been changed,
console.log(prakashDetails);
// Output: { state: "Haryana" , country: "India"}
Now this is called a shallow copy, where changing property of one object changes the property of another object as well.
We say that an object is a shallow copy
of another object if both the object point/refer to the same address in the memory location
Why did the above happen? 🧐
This happens because when we try to copy an object the newly copied object also refers to the same memory location of the existing object. That means from the above example, both the objects prakashDetails
and ashishDetails
are having the same address in the memory. Hence changing a property using the object ashishDetails
, will change the property in prakashDetails
as well.
To solve this JavaScipt has a built-in method Object.assign()
that will create a copy of the object, but at the same time if an object has a nested object then Object.assign()
will create a shallow copy for that nested object. Let's see this in action👇
Object.assign() 😎
Note: Object.assign(target, source)
-> all the property of source object will be copied to target object, target object can be an empty object or an already existing object
const prakashDetails = {
state: "Maharashtra",
country: "India"
}
const ashishDetails = Object.assign({}, prakashDetails);
console.log(ashishDetails)
// Output: { state: "Maharashtra", country: "India" }
// Changing state of object ashishDetails
ashishDetails.state = "Haryana";
console.log(ashishDetails)
// Output: { state: "Haryana" , country: "India" }
console.log(prakashDetails)
// Output: { state: "Maharashtra" , country: "India" }
Wooahh!! Object.assign()
did solve our problem, did it, no, it did not completely solve our problem, what If there is a nested object? Then in such cases object.assign()
will create a shallow copy of the nested object. Let's have a look at it👇
const prakashDetails = {
state: "Maharashtra",
country: "India",
location: {
city: "Mumbai",
pincode: "123456"
}
}
const ashishDetails = Object.assign({}, prakashDetails);
console.log(ashishDetails)
// Output: { state: "Maharashtra", country: "India", location: { city: "Mumbai", pincode: "123456"} }
// Changing state and city of object ashishDetails
ashishDetails.state = "Haryana";
ashishDetails.location.city = "Sirsa";
console.log(ashishDetails)
// Output: { state: "Haryana", country: "India", location: { city: "Sirsa", pincode: "123456"} }
console.log(prakashDetails);
// Output: { state: "Maharashtra", country: "India", location: { city: "Sirsa", pincode: "123456"} }
Damn it!! Even Object.assign()
couldn't create a deep copy
. If you see, the value of state are different but the value of city is the same, hence we can conclude that Object.assign()
also creates a shallow copy
.
How can we overcome this problem? 🧐🧐
To our rescue, we have something called a deep copy
. With deep copy
we copy the properties of an existing object to a new object, but the new object is created at a different address in the memory.
Deep copy
can be created in multiple ways
- By using
spread
operator. 💥 - By using
JSON.stringify()
andJSON.parse()
. 🔥 - By using
Lodash
💪💪💪
Let's see all the methods with examples👇
Spread Operator 🍻
const prakashDetails = {
state: "Maharashtra",
country: "India",
location: {
city: "Mumbai",
pincode: "123456"
}
}
// creating another object ashishDetails by using the spread
const ashishDetails = {...prakashDetails, location: {...prakashDetails.location}}
console.log(ashishDetails)
// Output: { state: "Maharashtra", country: "India", location: { city: "Mumbai", pincode: "123456"} }
// Changing state and city of object ashishDetails
ashishDetails.state = "Haryana";
ashishDetails.location.city = "Sirsa";
console.log(ashishDetails)
// Output: { state: "Haryana", country: "India", location: { city: "Sirsa", pincode: "123456"} }
console.log(prakashDetails);
// Output: { state: "Maharashtra", country: "India", location: { city: "Mumbai", pincode: "123456"} }
}
Ohh yess!! We got it, the spread operator
worked fine and created a deep copy
, but is it an optimised solution, certainly not, if there are multiple nested objects then we have to write the spread of each one of them and that is a time consuming affair.
Using JSON.stringify()
and JSON.parse()
💥💥
const prakashDetails = {
state: "Maharashtra",
country: "India",
location: {
city: "Mumbai",
pincode: "123456"
}
}
// The target object is first converted to a string output
const stringifiedOutput = JSON.stringify(prakashDetails);
// Stringified Object is parsed back to some other Object
const ashishDetails = JSON.parse(stringifiedOutput);
console.log(ashishDetails)
// Output: { state: "Maharashtra", country: "India", location: { city: "Mumbai", pincode: "123456"} }
// Changing state and city of object ashishDetails
ashishDetails.state = "Haryana";
ashishDetails.location.city = "Sirsa";
console.log(ashishDetails)
// Output: { state: "Haryana", country: "India", location: { city: "Sirsa", pincode: "123456"} }
console.log(prakashDetails);
// Output: { state: "Maharashtra", country: "India", location: { city: "Mumbai", pincode: "123456"} }
Yesss!!! This gives us the desired result we were waiting for, but, but, but this also has a CAVEAT
.
Disadvantage of using JSON.stringify()
and JSON.parse()
- If the property of an object is a function then
JSON.stringify()
andJSON.parse()
will not copy the function into another object Let's see an example👇
const greetMessage = {
objectType: "greetings"
getMessage: () => {
console.log("Hello Lovely People❤️ ")
}
}
const welcomeMessage = JSON.parse(JSON.stringify(greetMessage));
console.log(welcomeMessage);
// Output: { objectType: "greetings" }
Naah!!! There is again a problem, even JSON
couldn't give us a proper deep clone, since it cannot copy the function, so we cannot create a deep copy with JSON as well.
Finally presenting our hero --> Lodash 💪💪💪
Lodash
is a javascript module that has utility functions for strings, arrays, objects and much more. Do check Lodash documentation.
You need to install lodash in order to use it, or else you can test the code on Replit
var lodash = require('lodash');
const prakashDetails = {
state: "Maharashtra",
country: "India",
location: {
city: "Mumbai",
pincode: "123456"
},
greetMessage: () => console.log("Hello Lovely People❤️")
}
const ashishDetails = lodash.cloneDeep(prakashDetails);
ashishDetails.location.city = "Sirsa"
console.log(ashishDetails)
// Output: { state: "Haryana", country: "India", location: { city: "Sirsa", pincode: "123456"}, greetMessage: () => console.log("Hello Lovely People❤️") }
console.log(prakashDetails);
// Output: { state: "Maharashtra", country: "India", location: { city: "Mumbai", pincode: "123456"}, greetMessage: () => console.log("Hello Lovely People❤️") }
Finallyyy🍻, we got what we were searching for, the ultimate lodash
solves our problem.
Summary 😎
Shallow copy
means multiple objects referring to the same address in a memory location.Object.assign()
creates a shallow copy, it cannot be used to copy nested objectsspread operator
does the job but, then we have to spread each nested object which is a painJSON.stringify()
andJSON.parse()
does create a deep copy but does not copy function from one object to anotherLodash
handles all the above gracefully, it creates a deep copy by copying all the properties from the source object into the target object.
Reference 🙌
- Objects MDN - developer.mozilla.org/en-US/docs/Web/JavaSc..
- Objects JavaScript Info - javascript.info/object-basics
- Lodash Docs - lodash.com/docs/4.17.15
Thank you for reading the blog, if you understood and learned something new let me know in the comments, I will be happy to read what you have understood. Moreover, you can connect with me on Twitter and LinkedIn. Cheers 🍻