如何在JavaScript中深度复制自定义对象?

11

我已经在这里浏览了一段时间,但仍然没有找到适用于我的答案。

在JS中有没有办法深复制非平面对象?

我尝试过jQuery.extend(true, {}, this),但它只克隆了其中的一部分,其余部分仍然作为对另一个对象的引用。


1
但它只克隆了其中的一部分,其余部分仍然作为对另一个对象的引用。你能在问题中包含对象吗?可以创建stacksnippets来演示吗? - guest271314
总是有JSON.parse(JSON.stringify(...))这个技巧。 - Akshat Mahajan
1
如果是JSON格式,那很好,OP暗示它有函数。 - epascarello
@Robmeister2015,“jQuery.extend(true, {}, this)”中的“this”是什么? - guest271314
“非普通对象”是什么意思?你是指像DOM元素和函数之类的东西吗?如果要进行深度复制,那会意味着什么? - Barmar
这个回答解决了你的问题吗?如何在JavaScript中进行深度克隆 - Jannie Theunissen
6个回答

14

下面介绍三种不同的复制对象的方法。每种方法都有优缺点,所以请仔细阅读并选择最适合你情况的方法。

Object.assign 方法

使用 Object.assign 方法,它 "用于将一个或多个源对象的可枚举属性的值复制到目标对象中"。这将复制值和函数。截至撰写本文时,浏览器支持程度良好但不完美,但在我看来这是三种方法中最好的一种。

const obj1 = {a:1, b:2};
const obj1Copy = Object.assign(obj1)

扩展操作符方法

另外,您可以使用扩展操作符从一个对象展开到另一个对象。请注意,这将复制键的值,但如果某个键的值是内存地址(另一个嵌套对象或数组),则只会进行浅拷贝。

const obj1 = {a: () => {}, b:2}
const obj1Copy = { ...obj1 }

JSON stringify/parse 技巧

如果对象没有任何循环引用或函数作为值,您可以使用json.stringify技巧:

let myCopy = JSON.parse(JSON.stringify(myObject));

无需使用任何库,对大多数对象都能很好地工作。


这个复制函数会在 myObject 中工作吗? - guest271314
2
不是的。但是为什么要将函数添加到数据结构中呢?函数应该作为不同位置的例程。这只是面向对象编程的失败)) - Deep
@guest271314 function != value。克隆文档或窗口?绝对不寻常。好的。我理解例如window.onload,但这是观察者事件的回调。理论上,我们可以实现没有父对象的函数,将目标对象传递到第一个参数中,就像C89风格一样(例如onload(img,function(it){/.../}))。我只看到在使用结构中的函数时有一个好处--复制保护(例如Google地图)。责备惯例。现在所有开发人员都使用数据结构的函数而不是常规函数。 - Deep
@Deep 这是 var obj = {"0":function(){}},其中 "0" 是属性,obj["0"] 的值是 function(){}。或者这个例子的解释不正确吗?“克隆文档或窗口?绝对不寻常。”对于某些项目可能会有用,可以参考这个答案编辑、保存、自修改HTML文档;格式化生成的HTML、JavaScript - guest271314
  1. JSON.parse非常低效。
  2. 我不明白为什么在对象上使用方法是面向对象编程的失败。实际上,客户端最近的标准建议使用面向对象编程,这意味着使用方法。如果你在谈论数据对象,那就没问题了,但问题中没有提到这一点。
- Sasha
显示剩余5条评论

5
您可以使用Lodash的cloneDeep函数 - https://lodash.com/docs/4.16.4#cloneDeep 示例(来自文档):
var objects = [{ 'a': 1 }, { 'b': 2 }];

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false

2

1
考虑性能的快速深度克隆对象的方法。
JSON.parse(JSON.stringify({"foo":"bar"}))

性能如何? >> [ 也许这是深度复制对象的最佳方法 ]。我强烈建议您查看来自Google Chrome Developers社区在Youtube上的视频,解释了这种方法的工作原理和性能基准。

注意:如果您的对象没有包含日期、函数、未定义、无限大、正则表达式、映射、集合、Blob、FileList、ImageData、稀疏数组、类型化数组或其他复杂类型,请使用JSON.parse方法。 来源阅读此SO答案


小贴士 - React.JS 的初始状态树可以使用这个解决方案从 localStorage 中加载。


1
请注意,JSON.stringify/parse 始终创建一个普通对象,剥离原始对象中可能存在的任何自定义方法。要在 stringify/parse 后将方法还原到副本中,请使用 Object.setPrototypeOf(copy, Object.getPrototypeOf(original)) - terrymorse

0
如果你正在处理一个类实例,你可以使用类似这样的东西。
你不需要复制函数,因为它们被委托到原型上。

// myObject constructor
function myObject(foo, bar){
  this.foo = foo
  this.bar = bar
}
// delegate the functions to a prototype
myObject.prototype.something = function(){
  console.log('something')
}

function instanceCopy(obj) {
  // copy the object by the constructor
  const copy = new obj.constructor()
  const keys = Object.keys(obj)
  keys.forEach(key => {
    copy[key] = obj[key]
  })
  return copy
}

const myObj = new myObject('foo', 'bar')
const copyObj = instanceCopy(myObj)

console.log('myObj', myObj)
console.log('copyObj', copyObj)
console.log('same ?', copyObj === myObj)

// can we still call the functions
copyObj.something()
<script src="https://codepen.io/synthet1c/pen/WrQapG.js"></script>


-1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接