如何在React中深度克隆对象?

Spa*_*gCS 19 javascript reactjs

let oldMessages = Object.assign({}, this.state.messages);
// this.state.messages[0].id = 718

console.log(oldMessages[0].id);
// Prints 718

oldMessages[0].id = 123;

console.log(this.state.messages[0].id);
// Prints 123
Run Code Online (Sandbox Code Playgroud)

如何防止oldMessages成为参考,我想在oldMessages不改变值的情况下改变值state.messages

Ary*_*NYC 35

你需要做一个深层复制.Lodash的cloneDeep使这很简单:

import cloneDeep from 'lodash/cloneDeep';
const oldMessages = cloneDeep(this.state.messages);
oldMessages[0].id = 123;
Run Code Online (Sandbox Code Playgroud)

祝好运!


Dan*_*ith 9

首先让我们澄清一下浅克隆和深克隆的区别:

浅层克隆是克隆了其原始属性但其 REFERENCE 属性仍引用原始属性的克隆。

请允许我澄清:

let original = {
  foo: "brlja",
  howBigIsUniverse: Infinity,
  mrMethodLookAtMe: () => "they call me mr. Method",
  moo: {
   moo: "MOO"
  }
};

  // shallow copy
  let shallow = Object.assign({}, original);
  console.log(original, shallow); // looks OK

  shallow.moo.moo = "NOT MOO";

  console.log(original, shallow); // changing the copy changed the original
Run Code Online (Sandbox Code Playgroud)

请注意如何更改原始对象上反射的浅表副本的非原始属性的内部属性。

那么我们为什么要使用浅拷贝呢?

  • 这绝对是更快。
  • 它可以通过 1 个班轮在纯 JS 中完成。

你什么时候会使用浅拷贝?

  • 对象的所有属性都是基元

好的,让我们开始制作一个合适的(深度)副本。深拷贝显然应该将原始对象按值而不是引用处理到克隆中。当我们深入研究对象时,这应该会持续下去。因此,如果我们在原始属性中获得 X 级深度嵌套对象,它仍然应该是一个副本,而不是对内存中同一事物的引用。

大多数人建议滥用 JSON API。他们认为将一个对象变成一个字符串,然后通过它再回到一个对象会产生一个深拷贝。嗯,是的,也不是。让我们尝试这样做。

使用以下内容扩展我们的原始示例:

  let falseDeep = JSON.parse(JSON.stringify(original));
  falseDeep.moo.moo = "HEY I CAN MOO AGAIN";
  console.log(original, falseDeep); // moo.moo is decoupled
Run Code Online (Sandbox Code Playgroud)

看起来还行吧?错误的! 看看我从一开始就偷偷进入的mrMethodLookAtMehowBigIsUniverse属性发生了什么:)

一个返回 null,这绝对不是 Infinity,另一个是 GONE。那不是布埃诺。

简而言之:诸如 NaN 或 Infinity 之类的“更智能”值存在问题,它们被 JSON API 转换为 null。如果您使用:方法、RegExps、Maps、Sets、Blob、FileLists、ImageDatas、稀疏数组、Typed Arrays 作为原始对象的属性,还有更多问题。

简而言之。如果你告诉我你是一名中级 JS 开发人员,并且你建议我通过 JSON API 制作对象的深层副本,你将获得初级职位。如果你自称是大四学生,并认为 JSON API 是正确答案,我会告诉你面试结束了 :)

为什么?好吧,这会产生一些最讨厌的跟踪错误的方法。. 在 Typescript 成为一种东西之前,我做噩梦跟踪正在消失的方法或类型被转向另一个(它通过了某人的错误输入参数检查,但随后无法产生有效的结果)。

是时候总结一下了!那么正确答案是什么?

  • 您编写自己的深拷贝实现。我喜欢你,但当我们有截止日期时请不要这样做。
  • 使用已在项目中使用的库或框架提供给您的深度克隆功能。
  • Lodash 的 cloneDeep

许多人仍在使用 jQuery。所以在我们的例子中(请将 import 放在它所属的位置,在文件的顶部):

import jQ from "jquery"; 
let trueDeep = jQ.extend(true, original, {});
console.log(original, trueDeep);
Run Code Online (Sandbox Code Playgroud)

这是有效的,它是一个很好的深层副本,并且是单行的。但是我们必须导入整个 jQuery。对于一些它已经存在但对我来说我倾向于避免它,因为它过于臃肿并且命名非常不一致。

Angular 用户可以使用angular.copy(). 对于您的项目依赖项,如果它在那里,您可以 google :)

但是如果我的框架/库没有类似的功能怎么办?

你可以在JS库中使用我个人的SUPERSTAR(我不参与项目,只是一个大粉丝)-Lodash(或_为朋友)。

所以扩展我们的例子(再次注意导入的位置):

import _ from "lodash"; // cool kids know _ is low-dash
var fastAndDeepCopy = _.cloneDeep(objects);
console.log(original, lodashDeep);
Run Code Online (Sandbox Code Playgroud)

这是一个简单的oneliner,它有效,速度很快。

差不多就是这样:)

现在你知道 JS 中浅拷贝和深拷贝的区别了。您意识到 JSON API 滥用只是滥用,而不是真正的解决方案。如果您已经在使用 jQuery 或 Angular,那么您现在知道已经有适合您的解决方案。如果没有,您可以自己编写或考虑使用 lodash。

整个示例可以在这里找到: codeandbox -整个示例

  • 漂亮的答案,有一件事是,在浏览器中,滥用 JSON API(都是时间戳)后,日期看起来没有任何区别,但我不明白为什么这是一种滥用,也许将日期示例更改为空类或功能如你最后所说 (2认同)

小智 8

尝试使用

let tempVar = JSON.parse(JSON.stringify(this.state.statename))
Run Code Online (Sandbox Code Playgroud)

  • 请不要。JSON API 并不是为了实现此目的,并且此方法存在许多陷阱。请检查我的答案并了解如何正确执行。 (4认同)
  • 虽然这可行,但我怀疑如果您的数据结构无法用 JSON 表示(例如您的键之一是日期对象),它会遇到问题。 (3认同)