HiveBrain v1.2.0
Get Started
← Back to all entries
snippetjavascriptTip

How does JavaScript's structuredClone() differ from other cloning methods?

Submitted by: @import:30-seconds-of-code··
0
Viewed 0 times
javascriptstructuredclonefromhowothermethodsdoescloningdiffer

Problem

Cloning objects in JavaScript is a minefield of edge cases. Shallow copies break with nested data. JSON.stringify() drops functions and special types. Even custom deep clone functions may occasionally miss some details.
@Quick refresher
Luckily, structuredClone(), a rather new addition to JavaScript, provides a robust solution for deep cloning objects. Yet, the structured clone algorithm has its own quirks and limitations. In this article, we'll explore how it works, how it compares to other cloning methods, and when to use it.
<baseline-support featureId="structured-clone">
</baseline-support>

Solution

- `shallowClone` creates a **shallow copy** of the object, meaning nested objects are still references to the original.
- `deepClone` **recursively clones the object**, handling nested objects and arrays.
- `jsonClone` uses **`JSON.stringify()` and `JSON.parse()`** to create a deep clone, but it has limitations with various data types.
- `structuredClone()` is the **new built-in method** that uses the [structured clone algorithm](https://html.spec.whatwg.org/multipage/infrastructure.html#safe-passing-of-structured-data), which handles many edge cases that the other methods do not.

> [!NOTE]
>
> For a **detailed explanation of cloning methods**, see [the previous article on the topic](/js/s/shallow-deep-clone-object).

### Comparison table

<figure>

| Feature | shallowClone | deepClone | JSON | structuredClone |
|---|---|---|---|---|
| Nested objects/arrays | ⚠️ | ✅ | ✅ | ✅ |
| [Functions](#functions) | ⚠️ | ⚠️ | ❌  | ❌ <small>Error</small> |
| [Built-in types](#built-in-types) | ⚠️ | ❌<sup>[1]</sup> | ❌ | ✅ |
| [DOM nodes](#dom-nodes) | ⚠️ | ❌<sup>[2]</sup> | ❌ | ❌ <small>Error</small> |
| [Circular references](#circular-references) | ⚠️ | ❌<sup>[3]</sup> | ❌ <small>Error</small> | ✅ |
| [Prototype chain](#prototype-chain) | ❌ | ❌<sup>[4]</sup> | ❌ | ❌ |
| [Getters/Setters](#getters-and-setters) | ❌ | ❌<sup>[5]</sup> | ❌ | ❌ |
| [Symbol properties](#symbol-properties) | ✅ | ✅ | ❌ | ❌ |
| [Private fields](#private-fields) | ❌ | ❌ | ❌ | ❌ |

<figcaption>
Legend: ✅ = supported, ❌ = not supported, ⚠️ = shallow copy of reference, Error = throws an error<br/>
[1] Built-in types are often not cloned correctly, unless specifically handled</br>
[2] Cloning DOM nodes is inconsistent and can vary by implementation</br>
[3] Circular references will throw an error, unless specifically handled</br>
[4] The prototype chain could be preserved, depending on the implementation</br>
[5] Property descriptors can be preserved, if handled explicitly</br>
</figcaption>
</figure>

## Special cases

Given the previous table, let's explore each of the special cases in more detail, focusing on how `structuredClone()` differs from the other methods. We'll skip the nested object and arrays, as they are handled similarly by all methods except shallow cloning.

### Functions

Neither `structuredClone()` nor `JSON.stringify()` clone functions. Shallow and custom deep clones copy function **references**, without cloning the function itself. This means that the cloned object will still reference the original function, which can lead to unexpected behavior, especially combined with closures or `this` context.

@[You might also like](/js/s/closures)


Luckily, structuredClone(), a rather new addition to JavaScript, provides a robust solution for deep cloning objects. Yet, the structured clone algorithm has its own quirks and limitations. In this article, we'll explore how it works, how it compares to other cloning methods, and when to use it.
<baseline-support featureId="structured-clone">
</baseline-support>
Let's start by comparing the available cloning methods. For simplicity, we'll look at four common approaches, as shown below:
```js collapse={4-15}
const shallowClone = obj => ({ ...obj });

Code Snippets

- `shallowClone` creates a **shallow copy** of the object, meaning nested objects are still references to the original.
- `deepClone` **recursively clones the object**, handling nested objects and arrays.
- `jsonClone` uses **`JSON.stringify()` and `JSON.parse()`** to create a deep clone, but it has limitations with various data types.
- `structuredClone()` is the **new built-in method** that uses the [structured clone algorithm](https://html.spec.whatwg.org/multipage/infrastructure.html#safe-passing-of-structured-data), which handles many edge cases that the other methods do not.

> [!NOTE]
>
> For a **detailed explanation of cloning methods**, see [the previous article on the topic](/js/s/shallow-deep-clone-object).

### Comparison table

<figure>

| Feature | shallowClone | deepClone | JSON | structuredClone |
|---|---|---|---|---|
| Nested objects/arrays | ⚠️ | ✅ | ✅ | ✅ |
| [Functions](#functions) | ⚠️ | ⚠️ | ❌  | ❌ <small>Error</small> |
| [Built-in types](#built-in-types) | ⚠️ | ❌<sup>[1]</sup> | ❌ | ✅ |
| [DOM nodes](#dom-nodes) | ⚠️ | ❌<sup>[2]</sup> | ❌ | ❌ <small>Error</small> |
| [Circular references](#circular-references) | ⚠️ | ❌<sup>[3]</sup> | ❌ <small>Error</small> | ✅ |
| [Prototype chain](#prototype-chain) | ❌ | ❌<sup>[4]</sup> | ❌ | ❌ |
| [Getters/Setters](#getters-and-setters) | ❌ | ❌<sup>[5]</sup> | ❌ | ❌ |
| [Symbol properties](#symbol-properties) | ✅ | ✅ | ❌ | ❌ |
| [Private fields](#private-fields) | ❌ | ❌ | ❌ | ❌ |

<figcaption>
Legend: ✅ = supported, ❌ = not supported, ⚠️ = shallow copy of reference, Error = throws an error<br/>
[1] Built-in types are often not cloned correctly, unless specifically handled</br>
[2] Cloning DOM nodes is inconsistent and can vary by implementation</br>
[3] Circular references will throw an error, unless specifically handled</br>
[4] The prototype chain could be preserved, depending on the implementation</br>
[5] Property descriptors can be preserved, if handled explicitly</br>
</figcaption>
</figure>

## Special cases

Given the previous table, let's explore each of the special cases in more detail, focusing on how `structuredClone()` differs from the other methods. We'll skip the nested object and arrays, as they are handled similarly by all methods except shallow cloning.

### Functions

Neither `structuredClone()` nor `JSON.stringify()` clone functions. Shallow and custom deep clones copy function **references**, without cloning the function itself. This means that the cloned object will still reference the original function, which can lead to unexpected behavior, especially combined with closures or `this` context.

@[You might also like](/js/s/closures)
### Built-in types

Built-in objects, like `Date`, `Map`, `Set`, and `RegExp`, are not cloned correctly by `JSON.stringify()`. They are either converted to strings or empty objects. Shallow clones copy **references**, while custom deep clones may fail unless specifically handled. But, `structuredClone()` handles these types correctly, preserving their structure and properties when cloning them.

> [!WARNING]
>
> The structured clone algorithm handles `Error` types a little differently. You may want to check out [the official MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#error_types).
> [!IMPORTANT]
>
> **Almost all native types have straightforward constructor-based solutions you can use for cloning them**, if you can't use `structuredClone()`. You can add special handling for them in your custom deep clone function, if needed.

### DOM nodes

**DOM nodes cannot be cloned** with `structuredClone()`. Attempting to do so throws a `DataCloneError`. This makes sense when you consider what it means to clone a DOM node: it would require creating a new node in the document. Other options don't handle this gracefully either, but they don't throw errors, [which might actually be worse](/js/s/vocal-fails-silencing-errors).

Context

From 30-seconds-of-code: deep-clone-structured-clone

Revisions (0)

No revisions yet.