Object.assign() 创建的是深拷贝还是浅拷贝?

问题描述 投票:0回答:7

我刚刚想到了这个概念

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

它将原始对象创建到“

copy
”对象中。但是,我的问题是,这种克隆对象的方式会创建深拷贝还是浅拷贝?

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

javascript object deep-copy shallow-copy
7个回答
39
投票

忘记深层复制,如果您要复制的对象具有将

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

36
投票

通过使用

Object.assign()
,您实际上是在对对象进行浅复制。每当我们执行诸如将一个对象分配给另一个对象之类的操作时,我们实际上执行的是浅复制,即如果 OBJ1 是一个对象,则通过另一个对象(OBJ2)修改它也会反映 OBJ1 中的更改。


16
投票

对于小

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

14
投票

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

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

对于 redux 来说,

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


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

进行浅表复制,更改副本也会反映原始对象的更改。因此,要执行深度复制,我会推荐

lodash
cloneDeep

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

2
投票

Object.assign 仅创建浅复制。

const originalObject = {
        api : 'POST',
        contentType : 'JSON',
        userData : {
            name : 'Triver',
            email : '[email protected]'
        },
    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": "[email protected]"
    },
    "responseTime": "10ms"
}

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

注意: 下面是ES6浅拷贝的方式。

const originalObjectRef = {...originalObject};

希望这可以帮助别人,谢谢。


1
投票

如上所述,

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

© www.soinside.com 2019 - 2024. All rights reserved.