【问题标题】:JS: Does Object.assign() create deep copy or shallow copyJS:Object.assign() 是创建深拷贝还是浅拷贝
【发布时间】:2015-12-29 05:00:04
【问题描述】:

我刚刚遇到了这个概念

var copy = Object.assign({}, originalObject);

将原始对象的副本创建到“copy”对象中。但是,我的问题是,这种克隆对象的方式是创建深拷贝还是浅拷贝?

PS:令人困惑的是,如果它创建了一个深拷贝,那么这将是克隆对象的最简单方法。

【问题讨论】:

  • doc 是不言自明的,我猜:“Object.assign() 方法用于将所有 enumerable own 属性的值从一个或多个源对象复制到目标对象”
  • Lodash 的 _.clonedeep 做了一个深拷贝并按预期工作lodash.com/docs/4.17.11#cloneDeep

标签: javascript object deep-copy shallow-copy


【解决方案1】:

忘记深拷贝,即使浅拷贝也不安全,如果您要复制的对象有一个将enumerable 属性设置为false 的属性。

MDN:

Object.assign() 方法只复制可枚举和自己的属性 从源对象到目标对象

举个例子

var o = {};

Object.defineProperty(o,'x',{enumerable: false,value : 15});

var ob={}; 
Object.assign(ob,o);

console.log(o.x); // 15
console.log(ob.x); // undefined

【讨论】:

  • 这并不能完全回答问题。而是说如果它不可枚举会发生什么。
【解决方案2】:

通过使用Object.assign(),您实际上是在对您的对象进行浅拷贝。每当我们进行诸如将一个对象分配给另一个对象的操作时,我们实际上执行的是浅拷贝,即如果 OBJ1 是一个对象,则通过另一个对象 OBJ2 对其进行修改也会反映 OBJ1 的变化。

【讨论】:

  • 如果只做浅拷贝,Redux 是如何工作的?我认为它的全部意义在于对发送的数据进行深层复制,这样如果数据在商店之外发生更改,也不会改变商店中的内容。如果它是浅拷贝,那么数据将被链接,这会导致即使没有调度,数据也会改变存储中的内容,对吗?
  • 我在 Redux 中遇到了同样的问题,我最终去了 JSON.parse(JSON.stringify())。如果对象被另一个包变异并产生递归问题,这也会有问题。我正在寻找更好的选择。这个方法还是我用的。
【解决方案3】:

根据this paragraph from MDN创建一个浅拷贝:

对于深度克隆,我们需要使用其他替代方案,因为 Object.assign() 复制属性值。如果源值是 引用一个对象,它只复制该引用值。

对于 redux,Object.assign() 就足够了,因为 redux 应用的状态只包含不可变值 (JSON)。

【讨论】:

  • 应用程序的 redux 状态不能包含引用其他对象的对象..?
  • @VictorZamanian 绝对可以,因此 Object.assign 可能不是正确的工具,具体取决于数据结构。我发现 Immutability-helper 对于 Redux 状态特别有用。它只对您更改的内容执行深层复制,而对其他所有内容使用浅层复制。
【解决方案4】:

对于小的Data structures,我看到JSON.stringify()JSON.parse() 工作得很好。

// store as JSON
var copyOfWindowLocation = JSON.stringify(window.location)
console.log("JSON structure - copy:", copyOfWindowLocation)
// convert back to Javascript Object
copyOfWindowLocation = JSON.parse(copyOfWindowLocation)
console.log("Javascript structure - copy:", copyOfWindowLocation)

【讨论】:

  • 您的解决方案非常适合我。直到我阅读了这个简单的字符串化并解析回解决方案,我才感到沮丧。非常感谢。
  • 太棒了!很高兴它对你有所帮助
  • 小心。 JSON.stringify() ... JSON.parse() 去除设置为undefined 的对象属性,将Date 对象转换为日期字符串,并将正则表达式转换为{}。它会将原始对象的任何“类型”转换为普通对象。
【解决方案5】:

其他答案很复杂。
有些人根本不回答这个问题。

下面为我工作

// orignal object with deep keys
var originalObject = {
    k1: "v1",
    k2: "v2",
    deepObj: {
        k3: "v3",
        k4: "v4"
    }
};

// make copies now
var copy1 = JSON.parse(JSON.stringify(originalObject));
var copy2 = JSON.parse(JSON.stringify(originalObject));

希望对您有所帮助。

【讨论】:

    【解决方案6】:

    如上所述,Object.assign() 会进行浅层克隆,无法复制源对象的自定义方法,也无法复制带有enumerable: false 的属性。

    保留方法和不可枚举的属性需要更多代码,但不会更多。

    这将对数组或对象进行浅层克隆,复制源的方法和所有属性:

    function shallowClone(src) {
      let dest = (src instanceof Array) ? [] : {};
    
    // duplicate prototypes of the source
      Object.setPrototypeOf(dest, Object.getPrototypeOf(src));
    
      Object.getOwnPropertyNames(src).forEach(name => {
        const descriptor = Object.getOwnPropertyDescriptor(src, name);
        Object.defineProperty(dest, name, descriptor);
      });
      return dest;
    }
    

    例子:

    class Custom extends Object {
      myCustom() {}
    }
    
    const source = new Custom();
    source.foo = "this is foo";
    Object.defineProperty(source, "nonEnum", {
      value: "do not enumerate",
      enumerable: false
    });
    Object.defineProperty(source, "nonWrite", {
      value: "do not write",
      writable: false
    });
    Object.defineProperty(source, "nonConfig", {
      value: "do not config",
      configurable: false
    });
    
    let clone = shallowClone(source);
    
    console.log("source.nonEnum:",source.nonEnum);
    // source.nonEnum: "do not enumerate"
    console.log("clone.nonEnum:", clone.nonEnum);
    // clone.nonEnum: – "do not enumerate"
    
    console.log("typeof source.myCustom:", typeof source.myCustom);
    // typeof source.myCustom: – "function"
    console.log("typeof clone.myCustom:", typeof clone.myCustom);
    // typeof clone.myCustom: – "function"
    

    jsfiddle

    【讨论】:

    • 试试const original = { name: {f:""} }; const copied = shallowClone(original); copied.name.f = "m"; original.name.f 现在是“m”
    【解决方案7】:
    var copy = Object.assign({}, originalObject);
    

    改变副本的浅拷贝是否也反映了原始对象的变化。所以要执行深拷贝,我推荐lodash cloneDeep

    import cloneDeep from 'lodash/cloneDeep';
    var copy = cloneDeep(originalObject);
    

    【讨论】:

    • loadash 不是已经过时了吗?我读了一些关于它在 ES6 之后已经过时的博客
    猜你喜欢
    • 2011-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-30
    • 1970-01-01
    • 1970-01-01
    • 2010-11-19
    • 2013-08-23
    相关资源
    最近更新 更多