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

时间:2009-02-28 05:56:27

标签: javascript

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

当我第一次提出这个问题时,我只是喜欢

var clonedNodesArray = nodesArray.clone()

将存在并搜索有关如何在javascript中克隆对象的信息。我确实在StackOverflow上找到了question(由同样的@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);

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

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

34 个答案:

答案 0 :(得分:470)

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

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

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

答案 1 :(得分:220)

我解决了使用Object.assign

克隆一组对象的问题
const newArray = myArray.map(a => Object.assign({}, a));
使用spread syntax

或更短

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

答案 2 :(得分:158)

如果您只需要浅色副本,一个非常简单的方法是:

new_array = old_array.slice(0);

答案 3 :(得分:93)

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

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

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

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

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

答案 4 :(得分:43)

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

使用“......”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} ]

答案 5 :(得分:25)

这对我有用:

var clonedArray = $.map(originalArray, function (obj) {
                      return $.extend({}, obj);
                  });

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

var clonedArray = $.map(originalArray, function (obj) {
                      return $.extend(true, {}, obj);
                  });

答案 6 :(得分:18)

$.evalJSON($.toJSON(origArray));

答案 7 :(得分:8)

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

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

答案 8 :(得分: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

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

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

答案 9 :(得分:8)

Map将从旧数组创建新数组(不引用旧数组),并在地图内创建新对象并迭代properties(键)并将旧数组对象中的值赋值给与新对象相对应的属性。

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

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

答案 10 :(得分:7)

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

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);

答案 11 :(得分:6)

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

var clonedNodesArray = jQuery.extend([], nodesArray);

答案 12 :(得分:5)

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

我会使用变量copyCount来执行此操作,每次复制代码时变量都会增加1。将复制比当前复制过程低copyCount的对象。如果不是,则应引用已存在的副本。这使得必须从原始链接到其副本。

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

答案 13 :(得分:5)

为此,lodash具有cloneDeep功能:

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

答案 14 :(得分:4)

我的方法:

var temp = { arr : originalArray };
var obj = $.extend(true, {}, temp);
return obj.arr;

给了我一个漂亮,干净,深刻的原始数组克隆 - 没有任何对象被引用回原始数据: - )

答案 15 :(得分:4)

Array.slice可用于复制数组或数组的一部分。 http://www.devguru.com/Technologies/Ecmascript/Quickref/Slice.html 这将适用于字符串和数字.. - 更改一个数组中的字符串不会影响另一个 - 但对象仍然只是通过引用复制,因此对一个数组中引用的对象的更改将对另一个数组产生影响。

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

答案 16 :(得分:3)

我使用新的ECMAScript 6 Object.assign方法:

let oldObject = [1,3,5,"test"];
let newObject = Object.assign({}, oldObject);

此方法的第一个参数是要更新的数组, 我们传递一个空对象,因为我们想要一个新对象。

我们也可以使用这种语法,它相同但更短:

let newObject = [...oldObject];

答案 17 :(得分:3)

我对这个问题非常沮丧。显然,当您将通用数组发送到$ .extend方法时会出现问题。所以,要修复它,我添加了一些检查,它与泛型数组,jQuery数组和任何对象完美配合。

jQuery.extend({
    deepclone: function(objThing) {
        // return jQuery.extend(true, {}, objThing);
        /// Fix for arrays, without this, arrays passed in are returned as OBJECTS! WTF?!?!
        if ( jQuery.isArray(objThing) ) {
            return jQuery.makeArray( jQuery.deepclone($(objThing)) );
        }
        return jQuery.extend(true, {}, objThing);
    },
});

使用以下方式调用:

var arrNewArrayClone = jQuery.deepclone(arrOriginalArray);
// Or more simply/commonly
var arrNewArrayClone = $.deepclone(arrOriginalArray);

答案 18 :(得分:3)

忘记eval()(是JS最误用的功能,使代码变慢)和slice(0)(仅适用于简单数据类型)

对我来说这是最好的解决方案:

Object.prototype.clone = function() {
  var myObj = (this instanceof Array) ? [] : {};
  for (i in this) {
    if (i != 'clone') {
        if (this[i] && typeof this[i] == "object") {
          myObj[i] = this[i].clone();
        } else 
            myObj[i] = this[i];
        } 
    }
  return myObj;
};

答案 19 :(得分:3)

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

// 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'}]

答案 20 :(得分:2)

这会深度复制数组,对象,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;
}

答案 21 :(得分:2)

我们可以发明一种简单的递归Array方法来克隆多维数组。虽然嵌套数组中的对象保持对源数组中相应对象的引用,但数组不会。

Array.prototype.clone = function(){
  return this.map(e => Array.isArray(e) ? e.clone() : e);
};

var arr = [ 1, 2, 3, 4, [ 1, 2, [ 1, 2, 3 ], 4 , 5], 6 ],
    brr = arr.clone();
brr[4][2][1] = "two";
console.log(JSON.stringify(arr));
console.log(JSON.stringify(brr));

答案 22 :(得分:1)

在javascript中进行深度克隆的一些优雅方法

https://mootools.net/core/docs/1.6.0/Types/Object

https://scotch.io/bar-talk/copying-objects-in-javascript

1)用于克隆对象的vanilla Javascript方法

2)巧妙利用JSON库深度克隆对象

3)使用jQuery的$ .extend()函数

4)使用Mootools的clone()函数克隆对象

答案 23 :(得分:1)

以下代码将递归执行deep copying of objects and array

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;
}

Source

答案 24 :(得分:1)

使用jQuery:

var target= [];
$.each(source, function() {target.push( $.extend({},this));});

答案 25 :(得分:0)

取决于你是否拥有下划线或者Babel,这是深度克隆阵列的不同方式的基准。

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

看起来像巴贝尔是最快的。

var x = babel({}, obj)

答案 26 :(得分:0)

对于克隆对象,我只是建议ECMAScript 6 reduce()

const newArray=myArray.reduce((array, element)=>array.push(Object.assign({}, element)), []);

但坦率地说,我更喜欢@dinodsaurus的答案。我只是把这个版本作为另一种选择,但我个人将按照@dinodsaurus的建议使用map()

答案 27 :(得分:0)

UserPrincipal

答案 28 :(得分:0)

我认为设法编写一种深度克隆任何JavaScript结构的泛型方法,主要使用Object.create,这在所有现代浏览器中都是支持的。代码是这样的:

function deepClone (item) {
  if (Array.isArray(item)) {
    var newArr = [];

    for (var i = item.length; i-- !== 0;) {
      newArr[i] = deepClone(item[i]);
    }

    return newArr;
  }
  else if (typeof item === 'function') {
    eval('var temp = '+ item.toString());
    return temp;
  }
  else if (typeof item === 'object')
    return Object.create(item);
  else
    return item;
}

答案 29 :(得分:0)

在JavaScript中,数组和对象副本会更改原始值,因此深度复制是解决此问题的方法。

深层复制实际上意味着创建一个新数组并复制值,因为发生的任何事情都不会影响原始数组。

JSON.parseJSON.stringify是进行深度复制的最佳和简单方法。 JSON.stringify()方法将JavaScript值转换为JSON字符串。JSON.parse()方法解析JSON字符串,以构造JavaScript值或该字符串描述的对象。

//深度克隆

let a = [{ x:{z:1} , y: 2}];
let b = JSON.parse(JSON.stringify(a));
b[0].x.z=0

console.log(JSON.stringify(a)); //[{"x":{"z":1},"y":2}]
console.log(JSON.stringify(b)); // [{"x":{"z":0},"y":2}]

有关更多详细信息:Read Here

答案 30 :(得分:0)

这是我的解决方案,它适用于对象数组或Map。此解决方案还保留了方法。

深层复制实际上意味着创建一个新数组并复制值,因为发生的任何事情都不会影响原始数组。

这是最适合我的解决方案:

deepCopy(inputObj: any) {
    var newObj = inputObj;
    if (inputObj && typeof inputObj === "object") {
        newObj = Object.prototype.toString.call(inputObj) === "[object Array]" ? [] : {};
        for (var i in inputObj) {
            newObj[i] = this.deepCopy(inputObj[i]);
        }

        //For maps
        if(Object.prototype.toString.call(inputObj) === "[object Map]"){
            newObj = new Map;
            inputObj.forEach((v,k) =>{
                newObj.set(k,this.deepCopy(v));
            });
        }
    }
    return newObj;
}

答案 31 :(得分:0)

person1 = {
    name: 'Naved',
    last: 'Khan',
    clothes: {
        jens: 5,
        shirts: 10
    }
};

person2 = {
    name: 'Naved',
    last: 'Khan'
};

// first way  shallow copy single lavel copy 
// const person3 = { ...person1 };

// secound way shallow copy single lavel copy
// const person3 = Object.assign({}, person1);

// third  way shallow copy single lavel copy but old 
// const person3 = {};
// for (let key in person1) {
//  person3[key] = person1[key];
// }

// deep copy with array and object best way
const person3 = JSON.parse(JSON.stringify(person1));

    person3.clothes.jens = 20;

console.log(person1);
console.log(person2);
console.log(person3);

答案 32 :(得分:0)

我正在使用 VUE,因此数组/对象具有附加的 Vue 功能代码。我尝试了许多给出的答案,但最终使用了 clone-deep。

https://www.npmjs.com/package/clone-deep

答案 33 :(得分:-1)

function deepCloneArray(array) {
    return Array.from(Object.create(array));
}