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 sort a Set in JavaScript

ES6 introduced the set data structure in JavaScript. But sets are not ordered abstract data structures. So there is no .sort() property...

Debugging CSS scroll using one simple style

I have been doing a lot of complicated front-end work off lately and that always brings me back to the class conundrum...

CSS :has a parent selector now

CSS now includes a :has selector that allows us to apply styles on the basis of what is happening inside an element....

How to fix “invalid active developer path” after MacOS update

If you are here, then you are getting an "invalid active developer path" error on running commands in the terminal after a...

Getting the value of an input element as a number without parseInt

Every once in a while, you come across something and you think, how did I not know this earlier? valueAsNumber is that thing...

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.