How to clone anything in JavaScript

By

Learn how to deep clone anything in JavaScript with structuredClone(), why objects and arrays are copied by reference, and when you still need a polyfill.

~~~

The modern way to clone anything in JavaScript is structuredClone(). One function call, and you get a deep copy of objects, arrays, dates, maps, sets, whatever you throw at it.

We used to do all sorts of stuff in JavaScript land in regards to cloning.

Why?

Because ..references.

Primitive types (strings, numbers, booleans..) are always correctly “cloned” because they are passed by value.

Everything else (objects, arrays, dates, whatever) is an object. And objects are passed by reference.

Here’s what that means in practice:

const flavio = { age: 40 }
const copy = flavio

copy.age = 50
flavio.age //50

We didn’t copy anything. copy and flavio point to the same object, under two different names. Change one, you change the other.

So we had to do deep cloning using various ways, otherwise you end up with the same object reference, under a different name.

The old workarounds

The spread operator makes a copy, but only one level deep:

const original = { name: 'Flavio', address: { city: 'Milan' } }
const copy = { ...original }

copy.address.city = 'Rome'
original.address.city //'Rome'

The nested address object is still shared. That’s a shallow clone.

The other classic was the JSON round-trip:

const copy = JSON.parse(JSON.stringify(original))

This does clone deeply, but it mangles anything that isn’t JSON. Dates become strings. Functions and undefined values silently disappear. Maps and sets are lost.

Use structuredClone()

But it’s 2023 and we can use structuredClone():

const b = structuredClone(a)

This also deeply clones non-primitive types. Nested objects, arrays, dates, maps, sets, all copied properly. A Date stays a Date. Modify the clone all you want, the original is untouched.

What it can’t clone

Two limits worth knowing.

It throws a DataCloneError if the value contains a function anywhere. Functions can’t be cloned, and instead of dropping them silently like JSON did, it fails loudly.

And it doesn’t preserve prototypes. Clone an instance of a class you wrote, and you get back a plain object with the same properties, but instanceof checks won’t pass anymore.

For plain data, which is what you clone 99% of the time, none of this matters.

Just pay attention it’s a recent API, so if you use it in the browser make sure you use a build tool that provides core-js (babel) polyfills.

~~~

Related posts about js: