0% found this document useful (0 votes)
33 views

'Structuredclone ' - Deeply Copying Objects in JavaScript

StructuredClone(): deeply copying objects in JavaScript provides a concise 3 sentence summary: Copying objects via spreading in JavaScript is shallow, creating copies of the top levels but not deeper property values. A new function structuredClone() can be used to deeply copy objects across JavaScript platforms like browsers and Node.js, making independent copies of object properties and values rather than sharing references. StructuredClone() copies most built-in values but cannot copy functions or DOM nodes, and it may not preserve all property attributes of the original objects.

Uploaded by

jagruti patil
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
33 views

'Structuredclone ' - Deeply Copying Objects in JavaScript

StructuredClone(): deeply copying objects in JavaScript provides a concise 3 sentence summary: Copying objects via spreading in JavaScript is shallow, creating copies of the top levels but not deeper property values. A new function structuredClone() can be used to deeply copy objects across JavaScript platforms like browsers and Node.js, making independent copies of object properties and values rather than sharing references. StructuredClone() copies most built-in values but cannot copy functions or DOM nodes, and it may not preserve all property attributes of the original objects.

Uploaded by

jagruti patil
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 10

structuredClone(): deeply copying

objects in JavaScript
[2022-01-16] dev, javascript, jslang
(Ad, please don’t block)

Join us for AWS Innovate and learn


to scale your applications and
innovate with AWS Global
Infrastructure ADS VIA CARBON

Spreading is a common technique for copying objects in JavaScript:

Spreading into an Array literal to copy an Array


Spreading into an Object literal to copy a plain object

Spreading has one significant downside – it creates shallow copies: The top
levels are copied, but property values are shared.

structuredClone() is a new function that will soon be supported by most


browsers, Node.js and Deno. It creates deep copies of objects. This blog post
explains how it works.

1 On which JavaScript platforms is structuredClone() available?


2 Copying objects via spreading is shallow
3 Copying objects deeply via structuredClone()
4 Which values can structuredClone() copy?
4.1 Most built-in values can be copied
4.2 Some built-in values can’t be copied
4.3 Instances of user-defined classes become plain objects
4.4 The property attributes of copied objects
5 Sources of this blog post
6 Further reading

1 On which JavaScript platforms is


structuredClone() available?
Even though structuredClone() is not part of ECMAScript, it was added to
the platform-specific parts of many platforms and is still widely available
(either now or soon):

Chrome 98
Safari 137 (Technology Preview Release)
Firefox 94
Node.js 17.0
Deno 1.14

Tips:

structuredClone() isn’t always available in WebWorkers – check the


MDN browser compatibility table for more information.
On platforms that don’t support structuredClone, we can use a
polyfill.

2 Copying objects via spreading is shallow


One common way of copying Arrays and plain objects in JavaScript is via
spreading. This code demonstrates the latter:

const obj = {id: 'e1fd960b', values: ['a', 'b']};


const clone1 = {...obj};

Alas, this way of copying is shallow. On one hand, the key-value entry
clone1.id is a copy, so changing it does not change obj:

clone1.id = 'yes';
assert.equal(obj.id, 'e1fd960b');

On the other hand, the Array in clone1.values is shared with obj. If we


change it, we also change obj:

clone1.values.push('x');
assert.deepEqual(
clone1, {id: 'yes', values: ['a', 'b', 'x']}
);
assert.deepEqual(
obj, {id: 'e1fd960b', values: ['a', 'b', 'x']}
);
3 Copying objects deeply via
structuredClone()

Structured clone has the following typeSignature:

structuredClone(value: any): any

(This function has a second parameter which is rarely useful and beyond
the scope of this blog post. I couldn’t even replicate the use case that MDN
showed for it. For more information, see the MDN page for
structuredClone().)

structuredClone() copies objects deeply:

const obj = {id: 'e1fd960b', values: ['a', 'b']};


const clone2 = structuredClone(obj);

clone2.values.push('x');
assert.deepEqual(
clone2, {id: 'e1fd960b', values: ['a', 'b', 'x']}
);
assert.deepEqual(
obj, {id: 'e1fd960b', values: ['a', 'b']}
);

4 Which values can structuredClone() copy?

4.1 Most built-in values can be copied

Primitive values can be copied:

> typeof structuredClone(true)


'boolean'
> typeof structuredClone(123)
'number'
> typeof structuredClone('abc')
'string'

Most built-in objects can be copied – even though they have internal slots:
> Array.isArray(structuredClone([]))
true
> structuredClone(/^a+$/) instanceof RegExp
true

However, when copying a regular expression, property .lastIndex is


always reset to zero.

4.2 Some built-in values can’t be copied

Some built-in objects cannot be copied – structuredClone() throws a


DOMException if we try to do so:

Functions (ordinary functions, arrow functions, classes, methods)


DOM nodes

Demonstration of the former:

assert.throws(
() => structuredClone(function () {}), // ordinary function
DOMException
);
assert.throws(
() => structuredClone(() => {}), // arrow function
DOMException
);
assert.throws(
() => structuredClone(class {}),
DOMException
);

const objWithMethod = {
myMethod() {},
};
assert.throws(
() => structuredClone(objWithMethod.myMethod), // method
DOMException
);
assert.throws(
() => structuredClone(objWithMethod), // object with method
DOMException
);

What does the exception look like that is thrown by structuredClone()?

try {
structuredClone(() => {});
} catch (err) {
assert.equal(
err instanceof DOMException, true
);
assert.equal(
err.name, 'DataCloneError'
);
assert.equal(
err.code, DOMException.DATA_CLONE_ERR
);
}

4.3 Instances of user-defined classes become plain objects

In the following example, we copy an instance of the class C. The result,


clone, is not an instance of C.

class C {}
const clone = structuredClone(new C());

assert.equal(clone instanceof C, false);


assert.equal(
Object.getPrototypeOf(clone),
Object.prototype
);

To summarize – structuredClone() never copies the prototype chain of an


object:

Copies of built-in objects have the same prototypes as the originals.


Copies of instances of user-defined classes always have the prototype
Object.prototype (like plain objects).

4.4 The property attributes of copied objects


structuredClone() doesn’t always faithfully copy the property attributes of
objects:

Accessors are turned into data properties.


In copies, the property attributes always have default values.

Read on for more information.

4.4.1 Accessors become data properties

Accessors become data properties:

const obj = Object.defineProperties(


{},
{
accessor: {
get: function () {
return 123;
},
set: undefined,
enumerable: true,
configurable: true,
},
}
);
const copy = structuredClone(obj);
assert.deepEqual(
Object.getOwnPropertyDescriptors(copy),
{
accessor: {
value: 123,
writable: true,
enumerable: true,
configurable: true,
},
}
);

4.4.2 Copies of properties have default attribute values

Data properties of copies always have the following attributes:


writable: true,
enumerable: true,
configurable: true,

const obj = Object.defineProperties(


{},
{
accessor: {
get: function () {
return 123;
},
set: undefined,
enumerable: true,
configurable: true,
},
readOnlyProp: {
value: 'abc',
writable: false,
enumerable: true,
configurable: true,
},
}
);
const copy = structuredClone(obj);
assert.deepEqual(
Object.getOwnPropertyDescriptors(copy),
{
accessor: {
value: 123,
writable: true,
enumerable: true,
configurable: true,
},
readOnlyProp: {
value: 'abc',
writable: true,
enumerable: true,
configurable: true,
}
}
);

5 Sources of this blog post

Section “Safe passing of structured data” in the WHATWG HTML


standard
“The structured clone algorithm” on MDN
“structuredClone()” on MDN

6 Further reading

Chapter “Copying objects and Arrays” of “Deep JavaScript”


Chapter “Copying instances of classes: .clone() vs. copy constructors”
of “Deep JavaScript”
Chapter “Property attributes: an introduction” of “Deep JavaScript”

2ality – JavaScript and more Comment Policy


HTML tags work! Use <pre><code> or <pre> for code blocks, <code> for
inline code. 
3 Comments 2ality – JavaScript and more 🔒 Disqus' Privacy Policy

 Ashok Patil

 Favorite 2 t Tweet f Share Sort by Best

Join the discussion…

Samuel Reed −
7 months ago
It's great news that this is finally going to be available to developers. With
proper first-party engine support, should we expect `structuredClone()` to have
any performance wins over `_.deepClone()` and similar workarounds?
2△ ▽ Reply
john frazer −
7 months ago

Hi, thanks for your insights and thorough introduction. I really think much of
h i h ld i h MDN d i i h h f
what you write should go into the MDN documentation in one or the other form.

One nitpick though, you're using `assert.deepEqual()`, presumably as specified


in [CommonJS](http://wiki.commonjs.org/wi... und used in [NodeJS]
(https://nodejs.org/api/asse.... The problem with that standard is, IMHO, (1) the
mismatch between API terms like `equal` and the commonly used sense of
'equal' in mathematics and programming, and (2) the unwarranted split of
'equality testing' into two subfields, 'testing of equality of primitive values' and
'testing of equality of structured (compound) values'. To do away with point (2):
there shouldn't be any kind of 'deep' equality; two values are either equal, or
they're not equal, period (I could elaborate on that but let's not make this one
too long).

As for point (1), *'equal' in `assert` method names does not imply a method will
test for equality unless when preceded by the `strict` qualifier.*; in other
words: to test for equality, always use `assert.deepStrictEqual()`. Observe that
this method does also works for primitive values in a sane way, so *you never
need to use `assert.strictEqual()`. By contrast, `assert.equal()` and
`assert.deepEqual()` perform tests using the `==` operator, something that was
among the very first things Brandon Eich wanted to fix in the first TC39
meetings back in 1996 (but failed to). The result of this is that
`assert.deepEqual()` will pass for `1, '1'` and `[ 1 ], [ '1' ]`, which is most of the
time not what one wants I guess. If anything, `assert.equal()` etc. should have
been called `assert.equivalent()` or something along those lines. Also observe
that while `d = { '1': true }` and `d = { 1: true }` are indistinguishable, `'1' == 1`
is `true`, and `'a' + '1'` has the same result as `'a' + 1`, still, `1 + '1'` and `1 + 1`
are two totally different things (which is why many people shun implicit
coercions—there are just too many surprises).

I'm getting a bit lengthy here, but maybe it suffices to point out a discussion on
that subject: https://github.com/nodejs/n...

Happy to continue this if there's interest.


2△ ▽ Reply
Clem −
a month ago edited
While structuredClone() is not related to any API (like DOM or filesystem or
other) why it is not part of ECMAScript?

Is there anything that prevents such a function from being defined as an


ECMAScript standard?

Are there any plans to add this or a similar function as an ECMAScript standard?
△ ▽ Reply

✉ Subscribe d Add Disqus to your siteAdd DisqusAdd ⚠ Do Not Sell My Data

You might also like