你如何在Javascript中克隆一个对象数组?

wal*_*yqs 392 javascript

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

当我第一次想出这个问题时,我就是这样的

var clonedNodesArray = nodesArray.clone()
Run Code Online (Sandbox Code Playgroud)

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

var clonedNodesArray = jQuery.extend({}, nodesArray);
Run Code Online (Sandbox Code Playgroud)

克隆一个对象.我试过这个,但这只复制了数组中对象的引用.所以,如果我

nodesArray[0].value = "red"
clonedNodesArray[0].value = "green"
Run Code Online (Sandbox Code Playgroud)

nodesArray [0]和clonedNodesArray [0]的值都将变为"绿色".然后我试了一下

var clonedNodesArray = jQuery.extend(true, {}, nodesArray);
Run Code Online (Sandbox Code Playgroud)

哪个深层复制了一个Object,但我分别从Firebug和Opera Dragonfly 那里得到了" 过多的递归 "和" 控制堆栈溢出 "的消息.

你会怎么做?这是不应该做的事吗?在Javascript中是否有可重用的方法?

Vla*_*idi 500

来吧,这是21世纪:只要你的对象包含JSON可序列化的内容(没有函数,没有Number.POSITIVE_INFINITY等),就不需要任何循环来克隆数组或对象.这是一款纯粹的香草单线解决方案.

var clonedArray = JSON.parse(JSON.stringify(nodesArray))
Run Code Online (Sandbox Code Playgroud)

总结下面的注释,这种方法的主要优点是它还克隆了数组的内容,而不仅仅是数组本身.主要缺点是它仅限于处理JSON可序列化内容,以及它的性能(这比slice基于方法的情况要糟糕得多).

  • 这可能适用于JSON数据,但如果您的数组包含具有方法的任何函数或对象实例,请告别它们. (106认同)
  • 这通常是一种糟糕的方法,除非你的数组只包含基元,和/或它们本身只包含字符串/数字/布尔基元的对象(即使是`null`和`undefined`也会出问题,因为JSON不支持它们).此外,它的操作效率远远低于`old_array.slice(0);`,它应该更好,更快. (13认同)
  • 如果你有一个包含值Infinity的数组,请小心.此值丢失(之后为null).(http://jsfiddle.net/klickagent/ehm4bd3s/) (9认同)
  • 如果数组的对象具有DateTime,则将返回字符串而不是DateTime!新日期!== JSON.parse(JSON.stringify(新日期)) (2认同)
  • OP问题中的关键线,上面这个答案完全忽略了:_...其中每个对象也引用同一个数组中的其他对象?_ (2认同)
  • 虽然这对于懒惰的程序员来说是一个很好的解决方案,但效率相当低。现在是 21 世纪,并不意味着速度不重要。在我的测试中,循环数组并使用 Object.assign() 克隆每个对象的速度大约是原来的 4 倍。(例如,请参阅恐龙的回答) (2认同)

din*_*rus 251

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

__CODE__

甚至更短的扩展语法

__CODE__

  • 但如果myArray包含一堆恐龙,newArray包含一堆对象.那太蹩脚了,你不同意吗? (12认同)
  • @MatthewJamesDavis你可以通过用`new Dinosaur()`替换`{}`来解决这个问题. (12认同)
  • 这对一组对象非常有用,如果这些对象只包含原始属性......这就是我需要的,谢谢 (5认同)
  • 浅拷贝不深拷贝 (4认同)
  • 最好的方法,因为它保持对象函数活着,然后用JSON.parse丢失它们(JSON.stringify(nodesArray)) (2认同)
  • 这个稍微深一点,它为每个元素创建新的对象,但是如果对象包含对象本身,它们不会被复制,它们将是引用。 (2认同)

Leo*_*opd 157

如果你需要的只是一个浅拷贝,一个非常简单的方法是:

new_array = old_array.slice(0);
Run Code Online (Sandbox Code Playgroud)

  • 这实际上并不起作用,是吗?我的意思是,它不是如何克隆对象数组的问题的答案.这是克隆一个简单数组的解决方案. (104认同)
  • 实际上这不适用于对象数组.`slice`返回的数组将是一个新数组,但将包含对原始数组对象的引用. (33认同)
  • 我不认为你必须传递`0`,你至少可以在chrome中调用`.slice()` (5认同)
  • 对于实际上没有克隆的对象数组,更新到new_array也会更新old_array. (5认同)
  • 这仅适用于"generics"int,string等,但不适用于对象数组. (4认同)

Dan*_*Lew 99

浅拷贝的问题是没有克隆所有对象.虽然每个对象的引用在每个数组中都是唯一的,但是一旦你最终抓住它,你就会像以前一样处理同一个对象.克隆它的方式没有任何问题......使用Array.slice()会产生相同的结果.

您的深层复制存在问题的原因是因为您最终会得到循环对象引用.Deep会尽可能地深入,如果你有一个圆圈,它会一直无限地走,直到浏览器晕倒.

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

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

  1. 在第一轮中,复制不引用数组中其他对象的所有对象.跟踪每个对象的来源.
  2. 在第二轮,将对象链接在一起.


Men*_*Mez 48

最佳和最新的克隆方法如下:

使用"..."ES6扩展运算符.

这是最简单的例子:

var clonedObjArray = [...oldObjArray];
Run Code Online (Sandbox Code Playgroud)

这样我们将数组扩展为单个值,并使用[]运算符将其放入新数组中.

这是一个较长的示例,显示了它的不同工作方式:

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} ]
Run Code Online (Sandbox Code Playgroud)

  • 这只是深度复制数组,而不是数组中的每个对象. (46认同)
  • 要跟进@ToivoSäwén所说的内容,这不会深层复制数组中的对象.它仍会引用原始对象,因此如果你改变它们,它也会影响原始数组. (23认同)
  • 很好的现代答案,不适用于旧浏览器(如IE 11) (2认同)
  • 它仅适用于基本体。试试这个:objArray [0] .a = 3; 并且您会看到对象的引用在clonedArray中保持不变。 (2认同)

小智 25

这对我有用:

var clonedArray = $.map(originalArray, function (obj) {
                      return $.extend({}, obj);
                  });
Run Code Online (Sandbox Code Playgroud)

如果您需要数组中对象的深层副本:

var clonedArray = $.map(originalArray, function (obj) {
                      return $.extend(true, {}, obj);
                  });
Run Code Online (Sandbox Code Playgroud)

  • 这看起来会起作用。我试图避免大量使用 jQuery,所以我不会在我的情况下使用它,但是 for 循环和 for...in 会起作用。 (2认同)

小智 18

$.evalJSON($.toJSON(origArray));
Run Code Online (Sandbox Code Playgroud)

  • 没有JQ(在现代浏览器中很好):`JSON.parse(JSON.stringify(origArray));` (32认同)
  • `JSON.parse(JSON.stringify(origArray));`绝对是最简单的解决方案. (6认同)
  • 您将需要使用jquery json插件来使用此http://code.google.com/p/jquery-json/ (2认同)

sud*_*nna 14

如果要实现深度克隆,请使用JSON.parse(JSON.stringify(your {} or []))

const myObj ={
    a: 1,
    b: 2,
    b: 3
}

const deepClone = JSON.parse(JSON.stringify(myObj));
deepClone.a = 12;
console.log("deepClone-----"+myObj.a);
const withOutDeepClone = myObj;
withOutDeepClone.a = 12;
console.log("withOutDeepClone----" + myObj.a);
Run Code Online (Sandbox Code Playgroud)


Vin*_*oth 12

从2022年开始,我们可以使用structuredClone深复制。

structuredClone(array)
Run Code Online (Sandbox Code Playgroud)


eom*_*off 9

Map将从旧的数组创建新数组(不引用旧数组),并在地图内部创建新对象并迭代属性(键)并将旧Array对象的值分配给新对象的相应属性.

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

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


小智 8

我可能有一个简单的方法来做到这一点,而不必做痛苦的递归和不知道所讨论的对象的所有细节.使用jQuery,只需使用jQuery将对象转换为JSON $.toJSON(myObjectArray),然后获取JSON字符串并将其评估回​​对象.BAM!做完了!问题解决了.:)

var oldObjArray = [{ Something: 'blah', Cool: true }];
var newObjArray = eval($.toJSON(oldObjArray));
Run Code Online (Sandbox Code Playgroud)

  • 一些现代浏览器内置了JSON方法,因此您可以执行此操作:JSON.parse(JSON.stringify(MY_ARRAY)),它应该更快.好建议. (21认同)

boz*_*doz 8

我正在回答这个问题,因为似乎没有一个简单明确的解决方案来解决"在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
Run Code Online (Sandbox Code Playgroud)

此解决方案迭代数组值,然后迭代对象键,将后者保存到新对象,然后将该新对象推送到新数组.

jsfiddle.注:一个简单的.slice()[].concat()不足够的对象的数组.


Ste*_*tef 6

JQuery扩展工作正常,只需要指定克隆数组而不是对象(注意[]而不是{}作为extend方法的参数):

var clonedNodesArray = jQuery.extend([], nodesArray);
Run Code Online (Sandbox Code Playgroud)

  • 嗯,如果你对此有所了解,请你补充一下你为什么这样做的评论?或者,您可以先尝试代码,看看它是否有效?谢谢 ;) (2认同)
  • 更改第一个数组中的对象后,第二个数组中的对象也会被修改,所以这是不行的。 (2认同)

Geo*_*lly 5

正如Daniel Lew所提到的,循环图有一些问题.如果我遇到这个问题,我会为clone()有问题的对象添加特殊方法,或者记住我已经复制过的对象.

copyCount每次复制代码时,我都会使用一个变量增加1.copyCount复制具有低于当前复制过程的对象.如果不是,则应引用已存在的副本.这使得必须从原始链接到其副本.

还有一个问题:记忆.如果从一个对象到另一个对象有这个引用,浏览器可能无法释放这些对象,因为它们总是从某个地方引用.您必须进行第二次传递,将所有复制引用设置为Null.(如果你这样做,你就不必拥有copyCount一个布尔isCopied就足够了,因为你可以重置第二遍中的值.)


Raf*_*lli 5

此方法非常简单,您可以修改克隆而不修改原始数组。

// Original Array
let array = [{name: 'Rafael'}, {name: 'Matheus'}];

// Cloning Array
let clone = array.map(a => {return {...a}})

// Editing the cloned array
clone[1].name = 'Carlos';


console.log('array', array)
// [{name: 'Rafael'}, {name: 'Matheus'}]

console.log('clone', clone)
// [{name: 'Rafael'}, {name: 'Carlos'}]
Run Code Online (Sandbox Code Playgroud)

  • 这会执行两层深的浅复制,而“[...oldArray]”和“oldArray.slice(0)”则执行一层深的浅复制。所以这非常有用,但不是真正的完整深度克隆。 (2认同)

Dic*_*rus 5

lodash具有以下cloneDeep功能:

var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
Run Code Online (Sandbox Code Playgroud)