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

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

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


当前回答

此方法非常简单,您可以在不修改原始数组的情况下修改克隆。

//原始数组 让数组=[{名称:“拉斐尔”},{名称:' Matheus '}); //克隆阵列 让clone = array。Map (a => {return{…a}}) //编辑克隆数组 克隆[1].name = 'Carlos'; console.log(数组,数组) // [{name: 'Rafael'}, {name: 'Matheus'}] console.log(克隆,克隆) // [{name: 'Rafael'}, {name: 'Carlos'}]

其他回答

这将深度复制数组、对象、null和其他标量值,还将深度复制非本机函数上的任何属性(这很不常见,但也是可能的)。(为了提高效率,我们不尝试复制数组上的非数值属性。)

function deepClone (item) {
  if (Array.isArray(item)) {
    var newArr = [];
    for (var i = item.length; i-- > 0;) {
      newArr[i] = deepClone(item[i]);
    }
    return newArr;
  }
  if (typeof item === 'function' && !(/\(\) \{ \[native/).test(item.toString())) {
    var obj;
    eval('obj = '+ item.toString());
    for (var k in item) {
      obj[k] = deepClone(item[k]);
    }
    return obj;
  }
  if (item && typeof item === 'object') {
    var obj = {};
    for (var k in item) {
      obj[k] = deepClone(item[k]);
    }
    return obj;
  }
  return item;
}

我之所以回答这个问题,是因为似乎没有一个简单而明确的解决方案来解决“在JavaScript中克隆对象数组”的问题:

function deepCopy (arr) {
    var out = [];
    for (var i = 0, len = arr.length; i < len; i++) {
        var item = arr[i];
        var obj = {};
        for (var k in item) {
            obj[k] = item[k];
        }
        out.push(obj);
    }
    return out;
}

// test case

var original = [
    {'a' : 1},
    {'b' : 2}
    ];

var copy = deepCopy(original);

// change value in copy
copy[0]['a'] = 'not 1';

// original[0]['a'] still equals 1

该解决方案迭代数组值,迭代对象键,将后者保存到一个新对象,并将该新对象推入到一个新数组。

看到jsfiddle。注意:简单的.slice()或[].concat()对于数组中的对象是不够的。

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

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

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

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

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

正如Daniel Lew提到的,循环图有一些问题。如果我有这个问题,我要么向有问题的对象添加特殊的clone()方法,要么记住我已经复制了哪些对象。

我会用一个变量copyCount来做,它在你每次复制代码时增加1。copyCount小于当前复制进程的对象将被复制。如果不是,则应该引用已经存在的副本。这就需要从原文链接到副本。

还有一个问题:内存。如果您从一个对象引用到另一个对象,那么浏览器很可能无法释放这些对象,因为它们总是从某个地方引用。您必须进行第二次传递,将所有复制引用设置为Null。(如果你这样做,你不必有一个copyCount,但一个布尔iscopy就足够了,因为你可以在第二步重置值。)

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

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

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