Как вы клонируете массив объектов в 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 ]

6 голосов
/ 20 декабря 2012

JQuery extension работает нормально, просто вам нужно указать, что вы клонируете массив, а не объект ( обратите внимание на [] вместо {} в качестве параметра для метода расширения ):

var clonedNodesArray = jQuery.extend([], nodesArray);
5 голосов
/ 23 июля 2018

lodash имеет функцию cloneDeep для этих целей:

var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
5 голосов
/ 28 февраля 2009

Как отметил Дэниел Лью, у циклических графов есть некоторые проблемы. Если бы у меня была эта проблема, я бы либо добавил специальные методы clone() к проблемным объектам, либо запомнил, какие объекты я уже скопировал.

Я бы сделал это с переменной copyCount, которая увеличивается на 1 каждый раз, когда вы копируете свой код. Объект, который имеет copyCount ниже, чем текущий процесс копирования, копируется. Если нет, на копию, которая уже существует, следует ссылаться. Это приводит к необходимости ссылки с оригинала на его копию.

Есть еще одна проблема: память. Если у вас есть эта ссылка от одного объекта к другому, вероятно, браузер не сможет освободить эти объекты, так как на них всегда есть ссылки откуда-то. Вам нужно будет сделать второй проход, где вы установите все ссылки на копии в Null. (Если вы сделаете это, вам не понадобится copyCount, но будет достаточно логического isCopied, поскольку вы можете сбросить значение во втором проходе.)

4 голосов
/ 28 октября 2012

Мой подход:

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

дает мне хороший, чистый, глубокий клон исходного массива - ни один из объектов не ссылается на исходный: -)

4 голосов
/ 28 февраля 2009

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

3 голосов
/ 10 января 2012

забудь 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;
};
3 голосов
/ 04 июня 2012

Я был довольно разочарован этой проблемой. Очевидно, проблема возникает, когда вы отправляете универсальный массив в метод $ .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);
3 голосов
/ 04 мая 2017

Этот метод очень прост, и вы можете изменить свой клон без изменения исходного массива.

// 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'}]
3 голосов
/ 23 августа 2017

Я использую новый ECMAScript 6 Object.assign метод:

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

первый аргумент этого метода - массив для обновления, мы пропускаем пустой объект, потому что хотим получить новый объект.

мы также можем использовать этот синтаксис, который такой же, но более короткий:

let newObject = [...oldObject];
2 голосов
/ 04 октября 2016

Это глубоко копирует массивы, объекты, нулевые и другие скалярные значения, а также глубоко копирует любые свойства не встроенных функций (что довольно редко, но возможно). (Для эффективности мы не пытаемся копировать нечисловые свойства для массивов.)

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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...