'Structuredclone ' - Deeply Copying Objects in JavaScript
'Structuredclone ' - Deeply Copying Objects in JavaScript
objects in JavaScript
[2022-01-16] dev, javascript, jslang
(Ad, please don’t block)
Spreading has one significant downside – it creates shallow copies: The top
levels are copied, but property values are shared.
Chrome 98
Safari 137 (Technology Preview Release)
Firefox 94
Node.js 17.0
Deno 1.14
Tips:
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');
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()
(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().)
clone2.values.push('x');
assert.deepEqual(
clone2, {id: 'e1fd960b', values: ['a', 'b', 'x']}
);
assert.deepEqual(
obj, {id: 'e1fd960b', values: ['a', 'b']}
);
Most built-in objects can be copied – even though they have internal slots:
> Array.isArray(structuredClone([]))
true
> structuredClone(/^a+$/) instanceof RegExp
true
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
);
try {
structuredClone(() => {});
} catch (err) {
assert.equal(
err instanceof DOMException, true
);
assert.equal(
err.name, 'DataCloneError'
);
assert.equal(
err.code, DOMException.DATA_CLONE_ERR
);
}
class C {}
const clone = structuredClone(new C());
6 Further reading
Ashok Patil
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.
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...
Are there any plans to add this or a similar function as an ECMAScript standard?
△ ▽ Reply