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

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

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中是否有可重用的方法来做到这一点?


当前回答

下面的代码将递归地执行对象和数组的深度复制:

function deepCopy(obj) {
    if (Object.prototype.toString.call(obj) === '[object Array]') {
        var out = [], i = 0, len = obj.length;
        for ( ; i < len; i++ ) {
            out[i] = arguments.callee(obj[i]);
        }
        return out;
    }
    if (typeof obj === 'object') {
        var out = {}, i;
        for ( i in obj ) {
            out[i] = arguments.callee(obj[i]);
        }
        return out;
    }
    return obj;
}

其他回答

下面的代码将递归地执行对象和数组的深度复制:

function deepCopy(obj) {
    if (Object.prototype.toString.call(obj) === '[object Array]') {
        var out = [], i = 0, len = obj.length;
        for ( ; i < len; i++ ) {
            out[i] = arguments.callee(obj[i]);
        }
        return out;
    }
    if (typeof obj === 'object') {
        var out = {}, i;
        for ( i in obj ) {
            out[i] = arguments.callee(obj[i]);
        }
        return out;
    }
    return obj;
}

如果你只需要一个浅克隆,最好的方法如下:

使用…ES6扩展运算符。

这里有一个最简单的例子:

var clonedObjArray = [...oldObjArray];

通过这种方式,我们将数组扩展为单独的值,并使用[]操作符将其放入一个新数组中。

下面是一个更长的例子,展示了它的不同工作方式:

let objArray = [ {a:1} , {b:2} ]; let refArray = objArray; // this will just point to the objArray let clonedArray = [...objArray]; // will clone the array console.log( "before:" ); console.log( "obj array" , objArray ); console.log( "ref array" , refArray ); console.log( "cloned array" , clonedArray ); objArray[0] = {c:3}; console.log( "after:" ); console.log( "obj array" , objArray ); // [ {c:3} , {b:2} ] console.log( "ref array" , refArray ); // [ {c:3} , {b:2} ] console.log( "cloned array" , clonedArray ); // [ {a:1} , {b:2} ]

Map将从旧数组创建一个新数组(不引用旧数组),在Map中创建一个新对象,遍历属性(键),并将旧array对象的值赋给新对象的相应属性。

这将创建完全相同的对象数组。

let newArray = oldArray.map(a => {
               let newObject = {};
               Object.keys(a).forEach(propertyKey => {
                    newObject[propertyKey] = a[propertyKey];
               });
               return newObject;
});

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

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

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

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

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

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

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

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