如何深度克隆JavaScript对象

JavaScript提供了许多复制对象的方法,但并非所有方法都提供了深层复制。学习最有效的方法,并找出所有可用的选项

用JavaScript复制对象可能很棘手。有些方法执行浅表复制,这是大多数情况下的默认行为。

深拷贝与浅拷贝

浅表复制成功复制基本类型像数字和字符串一样,但是不会递归地复制任何对象引用,而是新复制的对象将引用相同的对象。

如果一个对象引用了其他对象,则在执行浅拷贝对象的,你复制参考到外部对象。

当执行一个深拷贝, 那些外部对象也被复制,因此,新的克隆对象完全独立于旧对象。

搜索如何在Internet上的JavaScript中深度克隆对象,您会找到很多答案,但是答案是并不总是正确的

最简单的选择:使用Lodash

我建议执行深度复制是依靠经过充分测试,非常流行且经过精心维护的库:Lodash。

Lodash提供了非常方便的clonedeepclone用于执行浅层和深层克隆的功能。

Lodash具有以下出色功能:您可以分别导入单个功能在您的项目中减少了很多依赖项。

在Node.js中:

const clone = require('lodash.clone')
const clonedeep = require('lodash.clonedeep')

这是一个显示正在使用的两个功能的示例:

const clone = require('lodash.clone')
const clonedeep = require('lodash.clonedeep')

const externalObject = { color: ‘red’, }

const original = { a: new Date(), b: NaN, c: new Function(), d: undefined, e: function () {}, f: Number, g: false, h: Infinity, i: externalObject, }

const cloned = clone(original)

externalObject.color = ‘blue’

console.info(‘⬇️ shallow cloning 🌈’) console.info( ‘✏️ Notice the i.color property we changed on original is also changed in the shallow copy’ ) console.log(original) console.log(cloned)

const deepcloned = clonedeep(original)

externalObject.color = ‘yellow’ console.log(’’) console.info(‘⬇️ deep cloning 🌈’) console.info(‘✏️ Notice the i.color property does not propagate any more’) console.log(original) console.log(deepcloned)

在这个简单的示例中,我们首先创建一个浅表副本,然后编辑i.color属性,该属性传播到复制的对象。

在深度克隆中,这不会发生。

Object.assign()

Object.assign()执行对象的浅表副本,而不是深层副本。

const copied = Object.assign({}, original)

作为浅表副本,将克隆值并复制对象引用(而不是对象本身),因此,如果在原始对象中编辑对象属性,则在复制的对象中也将对其进行修改,因为引用的内部对象是相同的:

const original = {
  name: 'Fiesta',
  car: {
    color: 'blue',
  },
}
const copied = Object.assign({}, original)

original.name = ‘Focus’ original.car.color = ‘yellow’

copied.name //Fiesta copied.car.color //yellow

使用对象传播运算符

点差算子是一个ES6 / ES2015该功能提供了执行浅层克隆的非常方便的方法,等效于Object.assign()做。

const copied = { ...original }

错误的解决方案

在网上您会发现很多建议。这是一些错误的:

使用Object.create()

注意:不推荐

const copied = Object.create(original)

这是错误的,它不执行任何复制。

相反,original对象被用作原型copied

显然,它可以工作,但是在幕后它却不是:

const original = {
  name: 'Fiesta',
}
const copied = Object.create(original)
copied.name //Fiesta

original.hasOwnProperty('name') //true
copied.hasOwnProperty('name') //false

查看更多Object.create()

JSON序列化

注意:不推荐

一些建议转换为JSON格式

const cloned = JSON.parse(JSON.stringify(original))

但这会带来意想不到的后果。

通过这样做,您将失去JSON中没有等效类型的任何Javascript属性,例如Function或者Infinity。分配给的任何属性undefined将被忽略JSON.stringify,导致它们在克隆对象上丢失。

此外,某些对象会转换为字符串,例如Date对象(同样,不考虑时区,默认为UTC),Set,Map等许多其他对象:

JSON.parse(
  JSON.stringify({
    a: new Date(),
    b: NaN,
    c: new Function(),
    d: undefined,
    e: function () {},
    f: Number,
    g: false,
    h: Infinity,
  })
)

Parsing as JSON

仅当您没有任何内部对象和函数,而只有值时,这才起作用。

免费下载我的JavaScript初学者手册


更多js教程: