…每个对象在同一个数组中也有对其他对象的引用?

当我第一次想到这个问题的时候,我就想到了

var clonedNodesArray = nodesArray.clone()

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

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

克隆对象。虽然我尝试了这个,但这只复制了数组中对象的引用。如果我

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

nodesArray[0]和clonedNodesArray[0]的值将显示为“绿色”。然后我尝试了

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

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

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


当前回答

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

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

如果使用扩展语法,甚至更短

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

其他回答

这取决于您使用的是Underscore.js还是Babel,下面是深度克隆数组的不同方式的基准测试。

https://jsperf.com/object-rest-spread-vs-clone/2

看起来巴别塔是最快的。

var x = babel({}, obj)
var game_popularity = [
     { game: "fruit ninja", popularity: 78 },
     { game: "road runner", popularity: 20 },
     { game: "maze runner", popularity: 40 },
     { game: "ludo", popularity: 75 },
     { game: "temple runner", popularity: 86 }
];
console.log("sorted original array before clonning");
game_popularity.sort((a, b) => a.popularity < b.popularity);
console.log(game_popularity);


console.log("clone using object assign");
const cl2 = game_popularity.map(a => Object.assign({}, a));
cl2[1].game = "clash of titan";
cl2.push({ game: "logan", popularity: 57 });
console.log(cl2);


// Adding new array element doesnt reflect in original array
console.log("clone using concat");
var ph = []
var cl = ph.concat(game_popularity);

// Copied by reference ?
cl[0].game = "rise of civilization";

game_popularity[0].game = 'ping me';
cl.push({ game: "angry bird", popularity: 67 });
console.log(cl);

console.log("clone using ellipses");
var cl3 = [...game_popularity];
cl3.push({ game: "blue whale", popularity: 67 });
cl3[2].game = "harry potter";
console.log(cl3);

console.log("clone using json.parse");
var cl4 = JSON.parse(JSON.stringify(game_popularity));
cl4.push({ game: "home alone", popularity: 87 });
cl4[3].game ="lockhead martin";
console.log(cl4);

console.log("clone using Object.create");
var cl5 = Array.from(Object.create(game_popularity));
cl5.push({ game: "fish ville", popularity: 87 });
cl5[3].game ="veto power";
console.log(cl5);


// Array function
console.log("sorted original array after clonning");
game_popularity.sort((a, b) => a.popularity < b.popularity);
console.log(game_popularity);


console.log("Object.assign deep clone object array");
console.log("json.parse deep clone object array");
console.log("concat does not deep clone object array");
console.log("ellipses does not deep clone object array");
console.log("Object.create does not deep clone object array");

输出

sorted original array before clonning
[ { game: 'temple runner', popularity: 86 },
{ game: 'fruit ninja', popularity: 78 },
{ game: 'ludo', popularity: 75 },
{ game: 'maze runner', popularity: 40 },
{ game: 'road runner', popularity: 20 } ]
clone using object assign
[ { game: 'temple runner', popularity: 86 },
{ game: 'clash of titan', popularity: 78 },
{ game: 'ludo', popularity: 75 },
{ game: 'maze runner', popularity: 40 },
{ game: 'road runner', popularity: 20 },
{ game: 'logan', popularity: 57 } ]
clone using concat
[ { game: 'ping me', popularity: 86 },
{ game: 'fruit ninja', popularity: 78 },
{ game: 'ludo', popularity: 75 },
{ game: 'maze runner', popularity: 40 },
{ game: 'road runner', popularity: 20 },
{ game: 'angry bird', popularity: 67 } ]
clone using ellipses
[ { game: 'ping me', popularity: 86 },
{ game: 'fruit ninja', popularity: 78 },
{ game: 'harry potter', popularity: 75 },
{ game: 'maze runner', popularity: 40 },
{ game: 'road runner', popularity: 20 },
{ game: 'blue whale', popularity: 67 } ]
clone using json.parse
[ { game: 'ping me', popularity: 86 },
{ game: 'fruit ninja', popularity: 78 },
{ game: 'harry potter', popularity: 75 },
{ game: 'lockhead martin', popularity: 40 },
{ game: 'road runner', popularity: 20 },
{ game: 'home alone', popularity: 87 } ]
clone using Object.create
[ { game: 'ping me', popularity: 86 },
{ game: 'fruit ninja', popularity: 78 },
{ game: 'harry potter', popularity: 75 },
{ game: 'veto power', popularity: 40 },
{ game: 'road runner', popularity: 20 },
{ game: 'fish ville', popularity: 87 } ]
sorted original array after clonning
[ { game: 'ping me', popularity: 86 },
{ game: 'fruit ninja', popularity: 78 },
{ game: 'harry potter', popularity: 75 },
{ game: 'veto power', popularity: 40 },
{ game: 'road runner', popularity: 20 } ]

Object.assign deep clone object array
json.parse deep clone object array
concat does not deep clone object array
ellipses does not deep clone object array
Object.create does not deep clone object array

数组中。Slice可以用来复制一个数组或数组的一部分…

这将与字符串和数字工作..-改变一个数组中的字符串不会影响另一个数组-但对象仍然只是通过引用复制,所以改变一个数组中引用的对象会影响另一个数组。

下面是一个JavaScript撤销管理器的示例,它可能对此很有用:http://www.ridgway.co.za/archive/2007/11/07/simple-javascript-undo-manager-for-dtos.aspx

如何使用一个简单的递归函数来获得对象/数组的深层副本

const deepCopyFunction = (inObject) => { const deepCopyObject = Array.isArray(inObject) ?[]: {}; for (inObject中的const键){ if (typeof inObject[key] === 'object') { deepCopyFunction (inObject(例子)); } deepCopyObject[key] = inObject[key]; } 返回deepCopyObject; }

浅拷贝的问题是所有对象都没有被克隆。虽然对每个对象的引用在每个数组中都是唯一的,但一旦最终获取到它,您将处理与以前相同的对象。你克隆它的方式没有问题…使用Array.slice()也会得到相同的结果。

深层复制出现问题的原因是您最终得到了循环对象引用。深度会尽可能地深,如果你有一个圈,它会无限延伸,直到浏览器昏厥。

如果数据结构不能表示为有向无环图,那么我不确定您是否能够找到用于深度克隆的通用方法。循环图提供了许多棘手的极端情况,由于这不是一个常见的操作,我怀疑是否有人编写了一个完整的解决方案(如果有可能的话——可能没有!)但是我现在没有时间来写一个严格的证明)。我在这个页面上找到了一些关于这个问题的很好的评论。

如果你需要一个带有循环引用的对象数组的深层副本,我相信你将不得不编写自己的方法来处理你的专用数据结构,这样它就是一个多通道克隆:

在第一轮中,克隆数组中不引用其他对象的所有对象。记录每个物体的起源。 在第二轮,把这些物体连在一起。