Object.assign()创建深层副本还是浅层副本

Does Object.assign() create a deep copy or a shallow copy?

本文关键字:副本 assign 创建 Object      更新时间:2023-09-26

我刚刚发现的概念

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

其将原始对象的副本创建到";CCD_ 1";对象然而,我的问题是,这种克隆对象的方式是创建深度复制还是创建浅层复制?

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

如果要复制的对象的属性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

使用Object.assign(),实际上是在对对象执行浅层复制。每当我们做一个操作,比如将一个对象分配给另一个对象时,我们实际上会执行一个浅层复制,即如果OBJ1是一个对象,通过另一个OBJ2对象修改它也会反映OBJ1的变化。

对于小型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)

2023现在有了一种官方的JavaScript方法来深度克隆对象structuredClone()

但我建议您阅读文档并进行一些测试,因为某些克隆不起作用。https://developer.mozilla.org/en-US/docs/Web/API/structuredClone

例如,这会抛出一个错误:

var clone = structuredClone(window.location) 

错误

VM673:1 Uncaught DOMException: Failed to execute 'structuredClone' on 'Window': Location object could not be cloned.
at <anonymous>:1:13

根据MDN:的这段话,它创建了一个浅拷贝

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

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

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

是否进行浅层复制,即更改副本也反映原始对象中的更改。因此,要执行深度复制,我建议使用lodash克隆深度

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

Object.assign仅创建浅拷贝。

const originalObject = {
        api : 'POST',
        contentType : 'JSON',
        userData : {
            name : 'Triver',
            email : 'postaction@yahoo.com'
        },
    responseTime: '10ms'
}
const originalObjectRef = Object.assign({}, originalObject);
originalObjectRef.contentType = 'XHTML';
originalObjectRef.userData.name = 'Red John';
console.log(originalObject);
Output:
{
    "api": "POST",
    "contentType": "JSON",
    "userData": {
        "name": "Red John",
        "email": "postaction@yahoo.com"
    },
    "responseTime": "10ms"
}

在浅拷贝中,引用变量主要存储它所引用的对象的地址。当一个新的引用变量被赋予旧引用变量的值时,存储在旧引用变量中的地址被复制到新引用变量中。这意味着新旧引用变量都指向内存中的同一对象。因此,如果对象的状态通过任何一个引用变量发生变化,则这两个变量都会反映出来。

注意:下面是ES6的浅层复制方式。

const originalObjectRef = {...originalObject};

希望这能帮助到别人,谢谢。

如上所述,copy0将执行浅层克隆,无法复制源对象的自定义方法,并且无法使用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