Be aware when cloning objects in JavaScript! π―ββοΈ

I'm a full-stack developer from South Africa πΏπ¦. I love writing about JavaScript, HTML and CSS.
Search for a command to run...

I'm a full-stack developer from South Africa πΏπ¦. I love writing about JavaScript, HTML and CSS.
Great and useful post! Thank you Chris. π Years ago I remember I used cloneDeep: the function from lodash, but it tooks a while to understand the problem. This pointer creation when use "=" it's not widely known unfortunately.
Hey Francesco, Haven't used the deepClone often myself, what exactly happens when you use the =? I mainly choose the JSON method or make my own deepClone specific for typecasting.
A great introduction to shallow vs deep cloning in JavaScript.
Be careful using JSON parse/stringify to deep clone as it does not support some JavaScript features when used to clone objects. Date objects will convert to strings. Functions, symbols, and undefined are also not correctly converted over.
Just a couple of things to watch out for if one is looking for a proper deep clone. Better off to use something like lodash cloneDeep for correctness and performance reasons.
Hey Jarrod,
Yes as mentioned in the article for robust deep-cloning one should use lodash deepclone or any other library.
For simple "flat" objects this JSON method works really well.
It does not work for Data objects for instance, they will forget that they are actual date objects.
Awesome Shamaayil, Thanks for taking the time to comment on my article, appreciate it π€
Most of you know me for my consistency, a golden arrow in my blog series. I've written 1000 articles in 1008 days! Almost an article a day, and my honeymoon was the only holiday I ever took. I'm super proud of this achievement; it has been a fantasti...

It's not the first time I'll be talking about community. I think it's an essential aspect of any successful tool. This shows in my previous explorations of Astro, Medusa, and now Vendure as well. All these products thrive in a super open, welcoming, ...

The cool part about Vendure is how easy it is to set up and how abstract each layer is. Basically, we get the following elements: External database Server Worker Admin UI Frontend While this is amazing, it also brings a bit of complexity when it co...

The previous article looked at customizing Vendure on a data and process level. In this article, we'll look at customizing emails, as they are often a big part of a webshop system. We'll be looking at two different layers of customization for customi...

Even though Vendure is a pretty significant project out of the box, in some cases, we might want to go in and modify some elements to work to our specific use case. In this article, I'll take a high-level look at some elements we can customize within...

Now and then you'll need to clone an object in JavaScript, mainly one to be modified but you still want the original to stay the same.
Let's say for this purpose we have a duplicate and change function.
Meaning we have an object, which we will duplicate and then change. The original, of course, will need to stay the same.
In JavaScript we can ofcourse clone a object by assigning it to a new const like this:
const original = { color: 'π΄', child: { action: 'stop' } };
const clone = original;
console.log(original); // {color: "π΄", child: {action: "stop"}}
console.log(clone); // {color: "π΄", child: {action: "stop"}}
Ok wow that works, cool done!
But not really, here comes the issue with this.
clone.color = 'π’';
console.log(original); // {color: "π’", child: {action: "stop"}}
console.log(clone); // {color: "π’", child: {action: "stop"}}
Hmm that's not cool, now our original one is also modified!
This is caused because of objects being reference types. When we choose to use = to clone, we make a pointer to object one instead of actually cloning it.
The spread operator is introduced in ES6 so fairly new, and not in the official specs yet!
To use it, you create a new object prefixed by three dots ...
const original = { color: 'π΄', child: { action: 'stop' } };
const spread = {...original};
console.log(original); // { color: 'π΄', child: { action: 'stop' } }
console.log(spread); // { color: 'π΄', child: { action: 'stop' } }
Ok, our basic clone works again, now let's check the change
spread.color = 'π’';
console.log(original); // { color: 'π΄', child: { action: 'stop' } }
console.log(spread); // { color: 'π’', child: { action: 'stop' } }
Yes, we did it!
But wait let's make sure we can also modify the child element.
spread.color = 'π’';
spread.child.action = 'start';
console.log(original); // { color: 'π΄', child: { action: 'start' } }
console.log(spread); // { color: 'π’', child: { action: 'start' } }
Wait, what just happened?
This is because the spread operator is a shallow copy, so only the first level will be actually copied, the rest will be assigned.
This is by far the method you will read the most about. It's basically the old version of the spread operator.
You can use it as follows:
const original = { color: 'π΄', child: { action: 'stop' } };
const assign = Object.assign({}, original);
console.log(original); // { color: 'π΄', child: { action: 'stop' } }
console.log(assign); // { color: 'π΄', child: { action: 'stop' } }
Cool, this also clones, but let's check if it modifies correctly.
assign.color = 'π’';
assign.child.action = 'start';
console.log(original); // { color: 'π΄', child: { action: 'start' } }
console.log(assign); // { color: 'π’', child: { action: 'start' } }
Damn, still the same issue turns out Object.assign is also a shallow copy.
So now what?
A quick and dirty hack to deep-clone is using JSON to stringify and then parse the object again.
This is not a "best-practice" but used by many people, and good enough for basic cloning.
For more robust deep-clone make use of packages like lodash cloneDeep.
It works like this:
const original = { color: 'π΄', child: { action: 'stop' } };
const json = JSON.parse(JSON.stringify(original));
console.log(original); // { color: 'π΄', child: { action: 'stop' } }
console.log(json); // { color: 'π΄', child: { action: 'stop' } }
Awesome, does the exact same thing, but let's see when we modify data.
json.color = 'π’';
json.child.action = 'start';
console.log(original); // { color: 'π΄', child: { action: 'stop' } }
console.log(json); // { color: 'π’', child: { action: 'start' } }
Yes, we did it! A fully cloned object that we can modify!
Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on Facebook or Twitter