使用 Spread 运算符的对象复制实际上是浅的还是深的?

新手上路,请多包涵

我了解传播运算符会制作对象的浅表副本,即克隆对象引用与原始对象相同的引用。但实际行为似乎自相矛盾和令人困惑。

 const oldObj = {a: {b: 10}};

const newObj = {...oldObj};

oldObj.a.b = 2;
newObj  //{a: {b: 2}}
oldObj  //{a: {b: 2}}

以上行为是有道理的,newObj 也通过更新 oldObj 来更新,因为它们引用相同的位置。

 const oldWeirdObj = {a:5,b:3};

const newWeirdObj = {...oldWeirdObj};

oldWeirdObj.a=2;
oldWeirdObj      //{a:2,b:3}
newWeirdObj   //{a:5,b:3}

我不明白,为什么 newWeirdObj 没有像 oldWeirdObj 那样更新?如果我没记错的话,他们仍然指的是同一个位置,但是为什么更新到 oldWeirdObj 而不是更新 newWeirdObj 呢?

原文由 richa Singh 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 445
2 个回答

所以,对于这个问题,你要明白什么是 shallow 拷贝和 deep 拷贝。

浅拷贝 是一个对象的逐位拷贝,它通过复制原始对象的内存地址来创建一个新对象。也就是说,它创建了一个内存地址与原始对象相同的新对象。

深拷贝,用动态分配的内存复制所有字段。也就是说,复制对象的每个值都会获得一个新的内存地址,而不是原始对象。

现在,差价操作员做什么?如果数据没有嵌套,它会深度复制数据。对于嵌套数据,它会深拷贝最顶层数据和浅拷贝嵌套数据。

在你的例子中,

 const oldObj = {a: {b: 10}};
const newObj = {...oldObj};

它深复制顶级数据,即它给属性 a 一个新的内存地址,但它浅复制嵌套对象即 {b: 10} 现在仍然指的是原来的 oldObj 的内存位置。

如果你不相信我检查这个例子,

 const oldObj = {a: {b: 10}, c: 2};
const newObj = {...oldObj};

oldObj.a.b = 2; // It also changes the newObj `b` value as `newObj` and `oldObj`'s `b` property allocates the same memory address.
oldObj.c = 5; // It changes the oldObj `c` but untouched at the newObj

console.log('oldObj:', oldObj);
console.log('newObj:', newObj);
 .as-console-wrapper {min-height: 100%!important; top: 0;}

您会看到 --- 的 newObj c 属性未受影响。

如何深度复制一个对象。

我认为有几种方法。一种常见且流行的方法是使用 JSON.stringify()JSON.parse()

 const oldObj = {a: {b: 10}, c: 2};
const newObj = JSON.parse(JSON.stringify(oldObj));

oldObj.a.b = 3;
oldObj.c = 4;

console.log('oldObj', oldObj);
console.log('newObj', newObj);
 .as-console-wrapper {min-height: 100%!important; top: 0;}

现在, newObj 有了全新的内存地址,对 oldObj 的任何更改都不会影响 newObj

另一种方法是将 oldObj 属性一个一个地分配到 newObj 的新分配属性中。

 const oldObj = {a: {b: 10}, c: 2};
const newObj = {a: {b: oldObj.a.b}, c: oldObj.c};

oldObj.a.b = 3;
oldObj.c = 4;

console.log('oldObj:', oldObj);
console.log('newObj:', newObj);
 .as-console-wrapper {min-height: 100%!important; top: 0;}

有一些库可用于深层复制。你也可以使用它们。

原文由 Sajeeb Ahamed 发布,翻译遵循 CC BY-SA 4.0 许可协议

Spread 语法在复制数组时有效地深入了一层。因此,它可能不适合复制多维数组,如以下示例所示。 (Object.assign() 也是一样)

 let x = [1,2,3];
let y = [...x];

y.shift(); // 1
console.log(x); // 1, 2, 3
console.log(y); // 2, 3

let a = [[1], [2], [3]];
let b = [...a];

b.shift().shift(); // 1

//  Oh no!  Now array 'a' is affected as well:
a
//  [[], [2], [3]]

原始文件

原文由 Phil 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题