代码之家  ›  专栏  ›  技术社区  ›  SpaceDogCS

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

  •  49
  • SpaceDogCS  · 技术社区  · 7 年前
    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
    

    如何预防 oldMessages 作为参考,我想更改 旧消息 不更改的值 state.messages

    4 回复  |  直到 6 年前
        1
  •  80
  •   Dharman Aman Gojariya    4 年前

    你需要做一份深度拷贝。 Lodash's cloneDeep 使这变得简单:

    import cloneDeep from 'lodash/cloneDeep';
    const oldMessages = cloneDeep(this.state.messages);
    oldMessages[0].id = 123;
    
        2
  •  32
  •   CodeSmith    3 年前

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

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

    请允许我澄清:

    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
    

    请注意,更改浅层副本的非基本体属性的内部属性如何反映在原始对象上。

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

    • 速度肯定更快。
    • 它可以通过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
    

    看起来还好吧? 错了! 看看发生在 mrMethodLookAtMe howBigIsUniverse 我从一开始就潜入的属性:)

    一个返回空值,它绝对不是无穷大,另一个则消失了。那不是布埃诺。

    简而言之: JSON API将“更智能”的值(如NaN或Infinity)设置为null时会出现问题。如果使用,则会出现更多问题: 方法、regexp、map、set、blob、filelist、ImageDatas、稀疏数组、类型化数组作为原始对象的属性。

    简言之如果您告诉我您是一名中级JS开发人员,并且建议我通过JSON API对对象进行深度复制,那么您将获得一个初级职位。如果你声称自己是大四学生,并认为JSON API是正确答案,我会告诉你面试结束:)

    为什么?好 这会产生一些最难闻的虫子。 . 我做过噩梦,梦见在Typescript成为一种东西之前,正在消失的方法或类型被转换成另一种(通过了某人糟糕的输入参数检查,但无法产生有效的结果)。

    该结束了!那么正确的答案是什么呢?

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

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

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

    这很有效,它是一个很好的深度复制,而且是一行。但我们必须导入整个jQuery。对一些人来说,它已经存在了,但对我来说,我倾向于避免它,因为它已经过度膨胀,而且命名非常不一致。

    角度用户可以使用 angular.copy() . 对于您的项目依赖关系,如果它在那里,您可以使用google进行搜索:)

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

    你可以在JS库中使用我的个人超级明星(我没有参与这个项目,只是一个超级粉丝)-Lodash(或朋友)。

    因此,请扩展我们的示例(再次注意导入的位置):

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

    这是一个简单的单行程序,它工作正常,速度很快。

    差不多就是这样:)

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

    整个示例可在此处找到: codesandbox - entire example

        3
  •  10
  •   Anjali    5 年前

    尝试使用

    let tempVar = JSON.parse(JSON.stringify(this.state.statename))
    
        4
  •  3
  •   Dharman Aman Gojariya    4 年前

    你到底在做什么

    let oldMessages = Object.assign({}, this.state.messages);
    

    是一个浅拷贝,类似于 {...this.state.message} 使用spread运算符。

    对象在内存中有自己的引用来销毁它,您可以使用 JSON.parse (JSON.stringify(object)) 无论键的嵌套程度如何,它都将删除对象的引用,您将获得一个新对象。

    这个概念称为深度复制或深度克隆。