如何在 JavaScript 中克隆对象数组?

2022-08-29 22:59:42

...其中每个对象还具有对同一数组中其他对象的引用?

当我第一次想到这个问题时,我只是想到了类似的东西

var clonedNodesArray = nodesArray.clone()

将存在并搜索有关如何在JavaScript中克隆对象的信息。我确实在Stack Overflow上找到了一个问题(由相同的@JohnResig回答),他指出使用jQuery你可以做到

var clonedNodesArray = jQuery.extend({}, nodesArray);

以克隆对象。我尝试了一下,这只复制数组中对象的引用。所以如果我

nodesArray[0].value = "red"
clonedNodesArray[0].value = "green"

两个节点Array[0]和克隆节点数组[0]的值将变成“绿色”。然后我试过了

var clonedNodesArray = jQuery.extend(true, {}, nodesArray);

它深度复制了一个对象,但我分别从FirebugOpera Dragonfly收到了“太多的递归”和“控制堆栈溢出”消息。

你会怎么做?这是不应该做的事情吗?有没有一种可重用的方法在JavaScript中做到这一点?


答案 1

使用结构化Clone创建深度副本

在JavaScript中深度复制数组的现代方法是使用结构化Clone

array2 = structuredClone(array1);

但是,此功能相对较新(Chrome 98,Firefox 94),目前仅对约85%的用户可用,因此如果没有polyfill,它还没有准备好投入生产。

作为替代方法,您可以使用下面支持良好的基于 JSON 的解决方案之一。

使用 JSON.parse 创建深度副本

考虑对象数组内所有可能对象的一般解决方案可能是不可能的。也就是说,如果您的数组包含具有 JSON 可序列化内容的对象(无函数、无等),则以性能成本避免循环的一种简单方法是这种纯香草单行解决方案。Number.POSITIVE_INFINITY

let clonedArray = JSON.parse(JSON.stringify(nodesArray))

总结一下下面的注释,此方法的主要优点是它还克隆数组的内容,而不仅仅是数组本身。主要的缺点是它只处理JSON可序列化内容的限制,并且它的性能比传播方法慢约30倍。

如果数组中有浅层对象,并且 IE6 是可以接受的,则更好的方法是将展开运算符与 .map 数组运算符结合使用。对于两级深度的情况(如下面附录中的数组):

clonedArray = nodesArray.map(a => {return {...a}})

原因有两个:1)它要快得多(请参阅下面的基准比较),并且它还将允许数组中的任何有效对象。

*附录:性能量化基于将此对象数组克隆一百万次:

 [{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic1.jpg?raw=true', id: '1', isFavorite: false}, {url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic2.jpg?raw=true', id: '2', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic3.jpg?raw=true', id: '3', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic4.jpg?raw=true', id: '4', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic5.jpg?raw=true', id: '5', isFavorite: true},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic6.jpg?raw=true', id: '6', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic7.jpg?raw=true', id: '7', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic8.jpg?raw=true', id: '8', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic9.jpg?raw=true', id: '9', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic10.jpg?raw=true', id: '10', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic11.jpg?raw=true', id: '11', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic12.jpg?raw=true', id: '12', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic13.jpg?raw=true', id: '13', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic14.jpg?raw=true', id: '14', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic15.jpg?raw=true', id: '15', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic16.jpg?raw=true', id: '16', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic17.jpg?raw=true', id: '17', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic18.jpg?raw=true', id: '18', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic19.jpg?raw=true', id: '19', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic20.jpg?raw=true', id: '20', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic21.jpg?raw=true', id: '21', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic22.jpg?raw=true', id: '22', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic23.jpg?raw=true', id: '23', isFavorite: false}]

使用:

let clonedArray = JSON.parse(JSON.stringify(nodesArray))

艺术

clonedArray = nodesArray.map(a => {return {...a}})

map/spread 方法每次传递需要 0.000466 毫秒,JSON.parse 和 JSON.stringify 每次传递需要 0.014771 毫秒。


答案 2

我用 Object.assign 解决了对象数组的克隆问题

const newArray = myArray.map(a => Object.assign({}, a));

或者使用跨页语法甚至更短

const newArray = myArray.map(a => ({...a}));