JavaScript

Deep copying in JavaScript using structuredClone

Advertisements

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.

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);
JavaScript

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
JavaScript

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));
JavaScript

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);
JavaScript

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.

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

Saransh Kataria

Born in Delhi, India, Saransh Kataria is the brain behind Wisdom Geek. Currently, Saransh is a software developer at a reputed firm in Austin, and he likes playing with new technologies to explore different possibilities. He holds an engineering degree in Computer Science. He also shares his passion for sharing knowledge as the community lead at Facebook Developer Circle Delhi, NCR which is a developer community in Delhi, India.

Share
Published by
Saransh Kataria

Recent Posts

Fixing cookies are blocked for a website with shields down on Brave

I recently switched completely to the Brave browser and have set ad blocking to aggressive…

4 months ago

Generating a QR code using Node.js

I was preparing a slide deck for a hackathon and decided to put in a…

5 months ago

How to clear the global npx cache

I have been using npx a lot lately, especially whenever I want to use a…

5 months ago

Copy/Pasting output from the terminal

Manually copy-pasting the output of a terminal command with a mouse/trackpad feels tedious. It is…

6 months ago

How To Get The Hash of A File In Node.js

While working on a project, I wanted to do an integrity check of a file…

7 months ago

Native popover API in HTML

Popovers have been a problem that was typically solved by using a third-party solution. But…

7 months ago
Advertisements