Deep Cloning Objects in JavaScript, the Modern Way

Read Time:4 Minute, 52 Second

Did you know, there’s now a native way in JavaScript to do deep copies of objects?

That’s right, this structuredClone function is built into the JavaScript runtime:

const calendarEvent = { title: "Builder.io Conf", date: new Date(123), attendees: ["Steve"]
} // 😍
const copied = structuredClone(calendarEvent)
Enter fullscreen mode Exit fullscreen mode

Did you notice in the example above we not only copied the object, but also the nested array, and even the Date object?

And all works precisely as expected:

copied.attendees // ["Steve"]
copied.date // Date: Wed Dec 31 1969 16:00:00
cocalendarEvent.attendees === copied.attendees // false
Enter fullscreen mode Exit fullscreen mode

That’s right, structuredClone can not only do the above, but additionally:

  • Clone infinitely nested objects and arrays
  • Clone circular references
  • Clone any transferrable objects, such as Date, Set, Map, Error, RegExp, ArrayBuffer, Blob, File, ImageData, and several more

So for example, this madness would even work as expected:

const kitchenSink = { set: new Set([1, 3, 3]), map: new Map([[1, 2]]), regex: /foo/, deep: { array: [ new File(someBlobData, 'file.txt') ] }, error: new Error('Hello!')
}
kitchenSink.circular = kitchenSink // ✅ All good, fully and deeply copied!
const clonedSink = structuredClone(kitchenSink)
Enter fullscreen mode Exit fullscreen mode

Why not just object spread?

It is important to note we are talking about a deep copy. If you just need to do a shallow copy, aka a copy that does not copy nested objects or arrays, then we can just do an object spread:

const simpleEvent = { title: "Builder.io Conf",
}
// ✅ no problem, there are no nested objects or arrays
const shallowCopy = {...calendarEvent}
Enter fullscreen mode Exit fullscreen mode

Or even one of these, if you prefer

const shallowCopy = Object.assign({}, simpleEvent)
const shallowCopy = Object.create(simpleEvent)
Enter fullscreen mode Exit fullscreen mode

But as soon as we have nested items, we run into trouble:

const calendarEvent = { title: "Builder.io Conf", date: new Date(123), attendees: ["Steve"]
} const shallowCopy = {...calendarEvent} // 🚩 oops - we just added "Bob" to both the copy *and* the original event
shallowCopy.attendees.push("Bob") // 🚩 oops - we just updated the date for the copy *and* original event
shallowCopy.date.setTime(456)
Enter fullscreen mode Exit fullscreen mode

As you can see, we did not make a full copy of this object.

The nested date and array are still a shared reference between both, which can cause us major issues if we want to edit those thinking we are only updating the copied calendar event object.

Why not JSON.parse(JSON.stringify(x)) ?

Ah yes, this trick. It is actually a great one, and is surprisingly performant, but has some shortcomings that structuredClone addresses.

Take this as an example:

const calendarEvent = { title: "Builder.io Conf", date: new Date(123), attendees: ["Steve"]
} // 🚩 JSON.stringify converted the `date` to a string
const problematicCopy = JSON.parse(JSON.stringify(calendarEvent))
Enter fullscreen mode Exit fullscreen mode

If we log problematicCopy, we would get:

{ title: "Builder.io Conf", date: "1970-01-01T00:00:00.123Z" attendees: ["Steve"]
}
Enter fullscreen mode Exit fullscreen mode

That’s not what we wanted! date is supposed to be a Date object, not a string.

This happened because JSON.stringify can only handle basic objects, arrays, and primitives. Any other type can be handled in hard to predict ways. For instance, Dates are converted to a string. But a Set is simply converted to {}.

JSON.stringify even completely ignores certain things, like undefined or functions.

For instance, if we copied our kitchenSink example with this method:

const kitchenSink = { set: new Set([1, 3, 3]), map: new Map([[1, 2]]), regex: /foo/, deep: { array: [ new File(someBlobData, 'file.txt') ] }, error: new Error('Hello!')
} const veryProblematicCopy = JSON.parse(JSON.stringify(kitchenSink))
Enter fullscreen mode Exit fullscreen mode

We would get:

{ "set": {}, "map": {}, "regex": {}, "deep": { "array": [ {} ] }, "error": {},
}
Enter fullscreen mode Exit fullscreen mode

Ew!

Oh yeah, and we had to remove the circular reference we originally had for this, as JSON.stringify simply throws errors if it encounters one of those.

So while this method can be great if our requirements fit what it can do, there is a lot that we can do with structuredClone (aka everything above that we failed to do here) that this method cannot.

Why not _.cloneDeep?

To date, Lodash’s cloneDeep function has been a very common solution to this problem.

And this does, in fact, work as expected:

import cloneDeep from 'lodash/cloneDeep' const calendarEvent = { title: "Builder.io Conf", date: new Date(123), attendees: ["Steve"]
} // ✅ All good!
const clonedSink = structuredClone(kitchenSink)
Enter fullscreen mode Exit fullscreen mode

But, there is just one caveat here. According to the Import Cost extension in my IDE, that prints the kb cost of anything I import, this one function comes in at a whole 17.4kb minified (5.3kb gzipped):

Deep Cloning Objects in JavaScript, the Modern Way

And that assumes you import just that function. If you instead import the more common way, not realizing that tree shaking doesn’t always work the way you hoped, you could accidentally import up to 25kb just for this one function 😱

Screenshot of the import cost of 'lodash' at 25kb

While that will not be the end of the world to anyone, it’s simply not necessary in our case, not when browsers already have structuredClone built in.

Browser and runtime support

And here is the best part – cloneDeep is supported in all major browsers, and even Node.js and Deno:

Screenshot of the browser support table linked to below

Source: MDN

Conclusion

It’s been a long time coming, but we finally now have structuredClone to make deep cloning objects in JavaScript a breeze. Thank you, Surma.

About me

Hi! I’m Steve, CEO of Builder.io.

We make a way to drag + drop with your components to create pages and other CMS content on your site or app, visually.

You can read more about how this can improve your workflow here.

You may find it interesting or useful:

Builder.io demo gif

Source: https://dev.to/builderio/deep-cloning-objects-in-javascript-the-modern-way-17kf

Tag Cloud

Java Java Logical Programs OTP Generation in Java python Recursion youtube video ASCII Upper and Lower Case blockchain javascript graph learn to code software development Successful Software Engineers breadth first search Java Array Programs Java Programs Uncategorized android ios programming kotlin web-development django data sql cybersecurity database swiftui serverless aws swift rust react background-position gradients loader mask grid nth-child pseudo elements indieweb WordPress Print Array without brackets C++ factorial Java String Programs Final Keyword Static Variable Axie Infinity Cryptokitties NFT games tool inserting MISC Tips Codes python code python projects python3 system info python project Bigginers How to Do Integrations Payment Gateways PHP checkout page in php Implement stripe payment gateway in Step by step in PHP integrate stripe gatway in php mysql payment gateway integration in php step by step payment gateway integration in php step by step with source code payment gateway integration in website PHP Integrate Stripe Payment Gateway Tutorial PHP shopping cart checkout code shopping cart in php stripe php checkout PHP/MySQL/JSON best international payment gateway does google pay accept international payments how to accept international payments in india paytm payment gateway razorpay codeigniter github razorpay custom checkout github razorpay get payment details razorpay integration in codeigniter github razorpay international payments Razorpay payment gateway integration in CodeIgniter razorpay payment gateway integration in php code Razorpay payment gateway integration with PHP and CodeIgniter Razorpay payment gateway setup in CodeIgniter Library & Frameworks Tips & Tricks UI/UX & Front-end coding birds online html code for google sign in login with google account in PHP login with google account using javascript login with google account using javascript codeigniter login with google account using php login with google account using php source code
How to Build a Real world Website Using HTML CSS JavaScript Previous post How to Build a Real world Website Using HTML CSS JavaScript
CLI tools you won’t be able to live without 🔧 Next post CLI tools you won’t be able to live without 🔧

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.