Deep copying in JavaScript using structuredClone

For as long as anyone can remember, deep copying in JavaScript was not a built-in feature and we had to resort to libraries or workarounds to create a deep copy of a JavaScript value.

That has changed now.

- Advertisement -

The platform now offers a built-in function that does deep copying for us: structuredClone()

Shallow copying

Before getting into deep copying, let us discuss what shallow copy is. The default behavior of copying in JavaScript is to shallow copy. That means that whenever we create a copy of something, changes to nested values in the original value will be reflected in the copied object as well.

let ingredients_list = ["noodles",{"list":["eggs","flour","water"]}];
let ingredients_list_copy = Array.from(ingredients_list);
ingredients_list_copy[1].list = ["rice flour","water"]

console.log(ingredients_list[1].list);

Even if we use the spread operator, it only goes one level deep. Deeply nested properties are still shallow copied.

const arr = [1, 2, 3];
const arr2 = [...arr]; // like arr.slice()

arr2.push(4);
//  arr2 becomes [1, 2, 3, 4]
//  arr remains unaffected because it is not deeply nested

const oldObj = {a: {b: 10}, c: 2};
const newObj = {...oldObj};

oldObj.a.b = 2; 
console.log(newObj.a.b);
// 2
// newObj `b` value gets updated as it is allocated at the same address

This happens because non-primitive types in JavaScript are handled as references by default. The act of copying them is merely the act of copying the reference to the underlying object. They get allocated the same memory address under the hood. This results in a shallow copy every time a non-primitive object is copied.

Deep copying

Deep copying is the opposite of shallow copying. All the nested properties are copied on an individual basis and whenever a nested property is found, the copying happens recursively, creating an actual copy of that property as well. This results in the copied object not sharing anything with the original object.

Until now, people either relied on Lodash’s cloneDeep() method to achieve deep copying, or the most common hack was:

const myDeepCopy = JSON.parse(JSON.stringify(myOriginal));

In fact, its popularity made the V8 engine optimize JSON.parse for making the above statement execute faster.

While it works amazingly well, there are a couple of shortcomings of that method:

  • It cannot handle objects that have recursive data structures, it throws an error when working with trees or linked lists
  • It discards functions
  • It does not work with built-in types like Map, Set, Date, Regexp, or ArrayBuffer. It throws an error if it encounters one of these as a value

Deep copying in JavaScript using structuredClone

The HTML spec was updated to expose a function called structuredClone() that creates deep copies of JavaScript values. The entire API is a single line:

const myDeepCopy = structuredClone(myOriginal);

And it does exactly what you would assume it to do!

There are a couple of limitations though:

  • Functions are still quietly discarded
  • Some values are not clonable, like Error and DOM nodes
  • An objects prototype chain is discarded as well, so for class instances, a plain instance of the class will be returned

But considering that it is a part of the HTML spec now, I guess we can rely on using structuredClone for deep copying in JavaScript until we run into one of the above limitations for some special use cases.

No longer do we need to reach out to other libraries or our good old friend JSON.parse for deep copying in JavaScript!

Recent Articles

How to debug better using $ sign shortcuts in Chrome devtools

Chrome devtools has a collection of utilities that can be used to perform common debugging tasks and make our life easier. The...

Deep copying in JavaScript using structuredClone

For as long as anyone can remember, deep copying in JavaScript was not a built-in feature and we had to resort to...

Chrome devtools: Using logpoints for logging messages directly

When it comes to debugging JavaScript in Chrome devtools, there are two different camps: the console.log fans and the debugger/breakpoint maximalist. I...

How to prevent npm install for unsupported Node.js versions

npm configurations allow us to do quite a lot of nifty things. One of them is to allow the project to set...

How to terminate a process on a port using the command line

Zombie processes are usually a pain to figure out. More often than not, I end up googling about how to terminate a...

Related Stories

Leave A Reply

Please enter your comment!
Please enter your name here

Hi there! Want some more knowledge?

Think that the knowledge shared is helpful? You might want to give our mailing list a try. We'll send you 2-4 emails a month, right when new posts come out.

Hi there! Want some more knowledge?

Think that the knowledge shared is helpful? You might want to give our mailing list a try. We'll send you 2-4 emails a month, right when new posts come out.