您如何在Javascript中克隆对象数组?

2020/09/27 18:01 · javascript ·  · 0评论

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

当我第一次想到这个问题时

var clonedNodesArray = nodesArray.clone()

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

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

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

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

nodeArray [0]和clonedNodesArray [0]的值都将变为“绿色”。然后我尝试

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

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

你会怎么做?这是什至不应该做的事情吗?有没有一种可重用的方式来做到这一点在Javascript中?

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

您的深层副本有问题的原因是因为您最终使用了循环对象引用。Deep会尽可能地深入,如果您有一个圆圈,它将无限循环直到浏览器晕倒为止。

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

如果您需要带有循环引用的对象数组的深层副本,我相信您将必须编写自己的方法来处理您的专用数据结构,例如多遍克隆:

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

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

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

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

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

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

甚至更短的传播语法

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

如果您需要的只是浅表副本,那么一个真正简单的方法是:

new_array = old_array.slice(0);

如果只需要克隆,则执行此克隆的最佳方法如下:

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

这对我有用:

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

如果您需要数组中对象的深层副本,请执行以下操作:

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

我可能有一种简单的方法来执行此操作,而不必进行痛苦的递归并且不知道所讨论对象的所有更详细的信息。使用jQuery,只需使用jQuery将您的对象转换为JSON $.toJSON(myObjectArray),然后获取您的JSON字符串并将其求值返回给对象即可。AM!做完了!问题解决了。:)

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

Map将从旧的数组创建新数组(不引用旧的数组),然后在地图内部创建新对象并遍历属性(键),并将旧Array对象中的值分配给与新对象相关的属性。

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

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

我之所以回答这个问题,是因为似乎没有一个简单而明确的解决方案来解决“用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()不足够的对象的数组。

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

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

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

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

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

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

正如Daniel Lew所述,循环图存在一些问题。如果遇到此问题,我可以clone()向有问题的对象添加特殊方法,或者记住我已经复制了哪些对象。

我会使用一个变量copyCount,每次在代码中复制时该变量都会增加1。copyCount复制的对象比当前复制过程的对象低如果没有,应参考已经存在的副本。这使得有必要从原始链接到其副本。

仍然存在一个问题:内存。如果您具有从一个对象到另一个对象的引用,则浏览器可能无法释放这些对象,因为它们始终是从某个地方引用的。您必须第二遍,将所有副本引用都设置为Null。(如果这样做,则不必使用a,copyCount但布尔值isCopied就足够了,因为您可以在第二遍中重置该值。)

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

我的方法:

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

给了我一个很好的,干净的,深层的原始数组的克隆-没有对象被引用回原始的:-)

lodash具有以下cloneDeep功能:

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

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

这个问题让我感到非常沮丧。当您将通用数组发送到$ .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);

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

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

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

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

我们还可以使用此语法,该语法相同但更短:

let newObject = [...oldObject];

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

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

JSON.parse并且JSON.stringify是进行深度复制的最佳和简单方法。JSON.stringify()方法将JavaScript值转换为JSON.parse()JSON字符串。该方法解析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}]

有关更多详细信息:在这里阅读

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

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

使用jQuery:

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

以下代码将递归执行对象和数组深层复制

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

资源

javascript深层克隆的一些优雅方法

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

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

1)克隆对象的普通Javascript方法

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

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

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

我认为设法编写了一种通用方法来深克隆任何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;
}

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

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

但坦率地说,我更喜欢@dinodsaurus的答案。我只是将此版本放在这里作为其他选择,但就我个人而言,我将按照map()@dinodsaurus的建议使用

取决于您是否具有Underscore或Babel,这是深度克隆数组的不同方法的基准。

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

看起来Babel是最快的。

var x = babel({}, obj)
function deepCloneArray(array) {
    return Array.from(Object.create(array));
}
本文地址:http://javascript.askforanswer.com/ninruhezaijavascriptzhongkelongduixiangshuzu.html
文章标签:
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!