Как вы клонируете массив объектов в Javascript? - PullRequest
365 голосов
/ 28 февраля 2009

... где каждый объект также имеет ссылки на другие объекты в том же массиве?

Когда я впервые столкнулся с этой проблемой, я просто подумал о чем-то вроде

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?

Ответы [ 30 ]

472 голосов
/ 06 мая 2014

Пока ваши объекты содержат JSON-сериализуемый контент (без функций, без Number.POSITIVE_INFINITY и т. Д.), Не требуется никаких циклов для клонирования массивов или объектов. Вот чистое ванильное однострочное решение.

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

Чтобы суммировать комментарии ниже, основное преимущество этого подхода заключается в том, что он также клонирует содержимое массива, а не только сам массив. Основными недостатками являются ограничение работы только с сериализуемым JSON-контентом и его производительность (что значительно хуже, чем у подхода, основанного на slice).

220 голосов
/ 27 октября 2016

Я решил клонирование массива объектов с помощью Object.assign

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

или даже короче с синтаксисом распространения

const newArray = myArray.map(a => ({...a}));
158 голосов
/ 09 ноября 2010

Если все, что вам нужно, это мелкая копия, то действительно простой способ:

new_array = old_array.slice(0);
94 голосов
/ 28 февраля 2009

Проблема с вашей мелкой копией заключается в том, что не все объекты клонированы. Хотя ссылки на каждый объект уникальны в каждом массиве, после того, как вы в конечном итоге ухватитесь за него, вы имеете дело с тем же объектом, что и раньше. Нет ничего плохого в том, как вы его клонировали ... тот же результат будет получен при использовании Array.slice ().

Причина, по которой у вашей глубокой копии возникают проблемы, заключается в том, что вы заканчиваете круговыми ссылками на объекты. Глубокий будет идти как можно глубже, и если у вас есть круг, он будет идти бесконечно, пока браузер не потеряет сознание.

Если структура данных не может быть представлена ​​в виде ориентированного ациклического графа, тогда я не уверен, что вы сможете найти универсальный метод для глубокого клонирования. Циклические графы предоставляют множество хитрых угловых случаев, и, поскольку это не обычная операция, я сомневаюсь, что кто-либо написал полное решение (если это вообще возможно - это может быть не так! Но у меня нет времени, чтобы попытаться написать строгое доказательство сейчас). Я нашел несколько хороших комментариев по этому вопросу на этой странице .

Если вам нужна глубокая копия массива объектов с циклическими ссылками, я полагаю, что вам придется написать собственный метод для обработки вашей специализированной структуры данных, такой, что это многопроходный клон:

  1. В первом раунде создайте клон всех объектов, которые не ссылаются на другие объекты в массиве. Следите за происхождением каждого объекта.
  2. Во втором раунде свяжите объекты вместе.
43 голосов
/ 03 января 2017

Лучший и самый современный способ сделать этот клон выглядит следующим образом:

Использование оператора распространения "..." 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} ]
25 голосов
/ 22 июня 2012

Это работает для меня:

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

А если вам нужна глубокая копия объектов в массиве:

var clonedArray = $.map(originalArray, function (obj) {
                      return $.extend(true, {}, obj);
                  });
18 голосов
/ 05 августа 2010
$.evalJSON($.toJSON(origArray));
8 голосов
/ 31 мая 2010

У меня может быть простой способ сделать это, не выполняя мучительную рекурсию и не зная всех мелких деталей рассматриваемого объекта. Используя jQuery, просто преобразуйте ваш объект в JSON, используя jQuery $.toJSON(myObjectArray), затем возьмите строку JSON и оцените ее обратно в объект. BAM! Готово и готово! Задача решена. :)

var oldObjArray = [{ Something: 'blah', Cool: true }];
var newObjArray = eval($.toJSON(oldObjArray));
8 голосов
/ 09 апреля 2018

Карта создаст новый массив из старого (без ссылки на старый), и внутри карты вы создадите новый объект и переберитее свойства (ключи) и назначите значения из старый объект Array для сопоставления свойств с новым объектом.

Это создаст точно такой же массив объектов.

let newArray = oldArray.map(a => {
               let newObject = {};
               Object.keys(a).forEach(propertyKey => {
                    newObject[propertyKey] = a[propertyKey];
               });
               return newObject ;
});
8 голосов
/ 31 декабря 2013

Я отвечаю на этот вопрос, потому что, кажется, нет простого и явного решения проблемы «клонирования массива объектов в 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() недостаточно для объектов в массиве.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...