const person1 = {
name: "Alex",
address: "Toronto"
}
const person2 = person1;
person2.name = "John";
console.log(person1);
What would be the output of the above code?
Does that look like this:
{
name: "Alex",
address: "Toronto"
}
You're wrong. It would look like this:
{
name: "John",
address: "Toronto"
}
Yes, that's the correct output. But that was not what we ought to do. We need the name of the person1 to stay like whatever it was before. We changed the name of person2.
Then, where did it go wrong?
The problem is with the JavaScript's object memory reference. We only assigned the person1
variable to the person2
variable. Javascript keeps the memory reference of the variable in the place for both of them. That's why when we changed the name of person2
, it affected person1
.
How to overcome this bug?
Even, I had spent a full day debugging a commercial app and banged my keyboards due to frustration to find out it's just a memory reference bug.
Here's a solution:
Spread operator
const person1 = {
name: "Alex",
address: "Toronto"
}
const person2 = {...person1};
person2.name = "John";
console.log(person1);
Here, we are spreading the person1
variable to create a new one. Now it would work fine as we expected. But, it has an exception.
What if our code was like this:
const person1 = {
name: {
first: "Alex",
last: "Telles"
},
address: "Toronto"
}
const person2 = {...person1};
person2.name.first = "John";
console.log(person1);
This is our expected output:
{
name: {
first: "Alex",
last: "Telles"
},
address: "Toronto"
}
But, what we get:
{
name: {
first: "John",
last: "Telles"
},
address: "Toronto"
}
The person1
also got changed even if we used spread operation to create a new object.
This is because, the spread operator only affects the first level properties and it won't clone the deep level key values. So, the first
in the name
property still has the memory reference to its parent.
Here is the solution to deep clone JS objects:
JSON stringify
const person2 = JSON.parse(JSON.stringify(person1));
Converting the object to string and then into JSON resolves the problem.
But still, we have a problem.
function test() { console.log("This is a Test") }
const person1 = {
name: {
first: "Alex",
last: "Telles"
},
f: test,
address: "Toronto"
}
person1.f()
const person2 = JSON.parse(JSON.stringify(person1));
person2.f()
This code will return an error saying TypeError: person2.f is not a function
. This is because, we had a memory reference to the function in the parent object and when we converted it into string for cloning, that reference got lost.
thanks to @efpage for the feedback.
So, there are some ways to overcome this:
Using loadash
const _ = require('lodash');
function test() { console.log("This is a Test") }
const person1 = {
name: {
first: "Alex",
last: "Telles"
},
f: test,
address: "XYZ"
}
const person2 = _.cloneDeep(person1);
person2.f();
lodash library has a cloneDeep function which will help to deep clone object with all memory reference.
Structured clone function
const person2 = structuredClone(person1);
The structuredClone
function will be available in the new version of JavaScript and it will deep copy the parent object.
Hope this articles helped you. Drop your feedbacks in comments.
Top comments (3)
'JSON.stringify' does not work if you use any memory references in your object:
Intrestingly, f is completely skipped in the output:
Yeah, that's a concern.
So,
structuredClone
is the only way.Is there any other workaround?
I am gonna edit my article with these later