JS StructuredClone:不是真正的深复制?

问题描述 投票:0回答:1

今天,在调查我们应用程序中的错误时,我目睹了 JavaScript

structuredClone
中非常令人惊讶的行为。

此方法承诺创建给定值的深度克隆。这是之前使用

JSON.parse(JSON.stringify(value))
技术实现的,在纸面上
structuredClone
似乎是该技术本身的超集,产生相同的结果,同时还支持日期和循环引用等内容。

但是,今天我了解到,如果您使用

structuredClone
克隆一个包含指向同一引用的引用类型变量的对象,这些引用将被保留,而不是使用不同的引用创建新值。

这是一个演示此行为的玩具示例:

const someSharedArray = ['foo', 'bar']

const myObj = {
  field1: someSharedArray,
  field2: someSharedArray,
  field3: someSharedArray,
}

const myObjCloned = structuredClone(myObj)

console.log(myObjCloned)
/**
{
    "field1": ["foo", "bar"],
    "field2": ["foo", "bar"],
    "field3": ["foo", "bar"],
}
**/

myObjCloned.field2[1] = 'baz'

// At this point:
// Expected: only `field2`'s value should change, because `myObjCloned` was deeply cloned.
// Actual: all fields' values change, because they all still point to `someSharedArray`

console.log(myObjCloned)
/**
{
    "field1": ["foo", "baz"],
    "field2": ["foo", "baz"],
    "field3": ["foo", "baz"],
}
**/

这是

structuredClone
非常令人惊讶的行为,因为:

  1. 声称可以进行深度克隆
  2. 使用时不会出现这种情况
    JSON.parse(JSON.stringify(value))

在我们的代码库中,我们总是使用

structuredClone
,但鉴于这一新发现,我正在考虑将所有
structuredClone
用法更改为
JSON.parse(JSON.stringify(value))
,以防止意外结果。

javascript json structured-clone
1个回答
0
投票

不是真正的深复制?

深拷贝。

正确的深拷贝应满足以下几个条件:

  1. 它应该将原始文件中的每个不同对象映射到结果中的一个不同对象:一对一映射。这也是确保支持循环引用的指导原则。

  2. 如果两个不同的属性具有相同的值 (

    Object.is(a, b) === true
    ),那么深度克隆中的这些属性也应该彼此相同。

在您的示例输入中有两个不同的对象:一个数组和一个(顶级)复杂对象。更进一步,

Object.is(myObj.field1, myObj.field2)
的结果是true。

您在示例中使用

structuredClone
得到的内容遵循这一点。值得注意的是,
Object.is(myObjCloned.field1, myObjCloned.field2)
是正确的。

期望得到什么(以及

JSON.parse(JSON.stringify(value))
返回什么)违反了这个原则:将创建三个不同的数组,这意味着same数组已被复制多次,并且不存在1对1不再映射了。前面提到的
Object.is
表达式的计算结果为 false。

另一个场景

让我们输入带有反向引用的输入:

const root = {};
root.arr = [root, root, root];

这里我们有一个对象和一个数组。后者包含对第一个对象的三个引用。同样在这里,我们期望对一个对象的这三个引用会产生另外三个引用,每个引用都引用唯一的克隆父对象。这与您的示例中发生的原理相同,只是共享引用恰好是父对象。

© www.soinside.com 2019 - 2024. All rights reserved.