structuredClone() 是一个新的函数,很快将被大多数浏览器、Node.js和Deno支持。它可以创建对象的深度拷贝。这篇博文解释了它是如何工作的。
structuredClone() 在哪些JavaScript平台上可用?
尽管structuredClone() 目前不是ECMAScript的一部分,但它被添加到了许多平台的特定部分,并且仍然可以广泛使用(现在或很快)。
此外,还有计划将此功能加入ECMAScript中。
提示。
通过传播复制对象是浅层次的
在JavaScript中复制Arrays和普通对象的一种常见方式是通过spreading。这段代码演示了后者。
const obj = {id: 'e1fd960b', values: ['a', 'b']};
const clone1 = {...obj};
唉,这种复制方式是浅层的。一方面,键值条目clone1.id 是一个副本,所以改变它并不会改变obj 。
clone1.id = 'yes';
assert.equal(obj.id, 'e1fd960b');
另一方面,clone1.values 中的数组是与obj 共享的。如果我们改变它,我们也会改变obj 。
clone1.values.push('x');
assert.deepEqual(
clone1, {id: 'yes', values: ['a', 'b', 'x']}
);
assert.deepEqual(
obj, {id: 'e1fd960b', values: ['a', 'b', 'x']}
);
通过structuredClone() 深入复制对象
结构化克隆有以下的类型标志。
structuredClone(value: any): any
(这个函数有第二个参数,很少有用,超出了本博文的范围。我甚至无法复制MDN为它展示的用例。欲了解更多信息,请参见MDN的structuredClone()页面)。
structuredClone() 拷贝对象的深度。
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']}
);
structuredClone() 可以复制哪些值?
大多数内置值可以被复制
原始值可以被复制。
> typeof structuredClone(true)
'boolean'
> typeof structuredClone(123)
'number'
> typeof structuredClone('abc')
'string'
大多数内置对象可以被复制--即使它们有内部槽。
> Array.isArray(structuredClone([]))
true
> structuredClone(/^a+$/) instanceof RegExp
true
然而,当复制正则表达式时,属性.lastIndex 总是被重置为零。
一些内置值不能被复制
一些内置对象不能被复制--如果我们试图这样做,structuredClone() 会抛出一个DOMException 。
- 函数(普通函数、箭头函数、类、方法)
- DOM节点
前者的演示。
structuredClone() 抛出的异常是什么样子的?
try {
structuredClone(() => {});
} catch (err) {
assert.equal(
err instanceof DOMException, true
);
assert.equal(
err.name, 'DOMException'
);
assert.equal(
err.code, DOMException.DATA_CLONE_ERR
);
}
用户定义的类的实例成为普通对象
在下面的例子中,我们复制了一个类的实例C 。其结果,clone ,不是C 的实例。
class C {}
const clone = structuredClone(new C());
assert.equal(clone instanceof C, false);
assert.equal(
Object.getPrototypeOf(clone),
Object.prototype
);
总结一下 -structuredClone() 永远不会复制一个对象的原型链。
- 内置对象的拷贝具有与原对象相同的原型。
- 用户定义的类的实例的拷贝总是有原型
Object.prototype(像普通对象一样)。
被复制对象的属性属性
structuredClone() 并不总是忠实地复制 属性属性的对象。
- 访问器被变成了数据属性。
- 在副本中,属性属性总是有默认值。
请继续阅读更多信息。
访问器变成数据属性
访问器变成了数据属性。
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,
},
}
);
属性的副本有默认的属性值
副本的数据属性总是有以下属性。
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,
}
}
);