Jak sklonować tablicę obiektów w JavaScript?

421

... gdzie każdy obiekt ma również odniesienia do innych obiektów w tej samej tablicy?

Kiedy po raz pierwszy wpadłem na ten problem, pomyślałem o czymś takim

var clonedNodesArray = nodesArray.clone()

istniałby i szukał informacji na temat klonowania obiektów w javascript. Znalazłem pytanie na StackOverflow (odpowiedział ten sam @JohnResig) i wskazał, że z jQuery możesz zrobić

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

sklonować obiekt. Próbowałem tego jednak, to tylko kopiuje odwołania do obiektów w tablicy. Więc jeśli ja

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

wartości zarówno nodesArray [0], jak i clonedNodesArray [0] okażą się „zielone”. Potem spróbowałem

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

który głęboko kopiuje obiekt, ale dostałem komunikaty o „ zbyt dużej rekurencji ” i „ przepełnieniu stosu kontrolnego ” odpowiednio od Firebug i Opera Dragonfly.

Jak byś to zrobił? Czy to coś, czego nawet nie należy robić? Czy istnieje sposób wielokrotnego użytku w JavaScript?

wallyqs
źródło

Odpowiedzi:

106

Problem z płytką kopią polega na tym, że wszystkie obiekty nie są klonowane. Podczas gdy odniesienia do każdego obiektu są unikalne w każdej tablicy, gdy ostatecznie go złapiesz, masz do czynienia z tym samym obiektem, co poprzednio. Nie ma nic złego w sposobie sklonowania ... ten sam wynik wystąpiłby przy użyciu Array.slice ().

Powodem, dla którego twoja głęboka kopia ma problemy, jest to, że kończysz się na okrągłych odnośnikach do obiektów. Głębokość zejdzie tak głęboko, jak to możliwe, a jeśli masz koło, będzie jeździć nieskończenie, dopóki przeglądarka nie zemdleje.

Jeśli struktury danych nie można przedstawić jako ukierunkowanego wykresu acyklicznego, nie jestem pewien, czy będziesz w stanie znaleźć uniwersalną metodę głębokiego klonowania. Cykliczne wykresy dostarczają wielu skomplikowanych przypadków narożnych, a ponieważ nie jest to powszechna operacja, wątpię, aby ktokolwiek napisał pełne rozwiązanie (jeśli to w ogóle możliwe - może nie być! Ale nie mam czasu, aby spróbować teraz napisać rygorystyczny dowód). Na tej stronie znalazłem kilka dobrych komentarzy na ten temat .

Jeśli potrzebujesz głębokiej kopii tablicy obiektów z referencjami cyklicznymi, uważam, że będziesz musiał kodować własną metodę obsługi specjalnej struktury danych, tak aby była to klon wieloprzebiegowy:

  1. W pierwszej rundzie utwórz klon wszystkich obiektów, które nie odwołują się do innych obiektów w tablicy. Śledź pochodzenie każdego obiektu.
  2. W drugiej rundzie połącz obiekty ze sobą.
Dan Lew
źródło
1
Naprawiono link do odpowiedzi @PatrickdeKleijn: web.archive.org/web/20140222022056/http://my.opera.com/…
Mike Szyndel
531

Tak długo, jak obiekty zawierają treści serializowane przez JSON (bez funkcji, nie Number.POSITIVE_INFINITYitp.), Nie ma potrzeby, aby pętle klonowały tablice lub obiekty. Oto jedno-liniowe rozwiązanie czysto waniliowe.

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

Podsumowując poniższe komentarze, podstawową zaletą tego podejścia jest to, że klonuje również zawartość tablicy, a nie tylko samą tablicę. Podstawowymi wadami jest ograniczenie pracy tylko nad treściami serializowanymi w JSON i jego wydajność (która jest znacznie gorsza niż slicepodejście oparte na podejściu).

Vladimir Kharlampidi
źródło
118
Może to działać w przypadku danych JSON, ale jeśli tablica zawiera jakieś funkcje lub instancje obiektów posiadających metody, pożegnaj się z nimi.
sp0rkyd0rky
12
bądź ostrożny, jeśli masz tablicę zawierającą wartość Nieskończoność. Ta wartość zostaje utracona (później jest zerowa). ( jsfiddle.net/klickagent/ehm4bd3s )
klickagent.ch
13
Jest to ogólnie złe podejście, chyba że tablica zawiera tylko prymitywy i / lub obiekty, które same zawierają tylko prymitywy ciąg / liczba / wartość logiczna (nawet nulli undefinedbędą to problemy, ponieważ JSON ich nie obsługuje). Co więcej, jest to znacznie mniej wydajna operacja niż old_array.slice(0);, która powinna działać zarówno lepiej, jak i szybciej.
XML
2
jeśli obiekt tablicy ma DateTime, to zamiast DateTime zostanie zwrócony ciąg! nowa data! == JSON.parse (JSON.stringify (nowa data))
MarkosyanArtur
2
Kluczowa linia w pytaniu PO, której powyższa odpowiedź całkowicie ignoruje: ... gdzie każdy obiekt ma również odniesienia do innych obiektów w tej samej tablicy?
XML
288

Rozwiązałem klonowanie tablicy obiektów za pomocą Object.assign

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

lub nawet krótszy ze składnią rozprzestrzeniania

const newArray = myArray.map(a => ({...a}));
dinodsaurus
źródło
15
Ale jeśli myArray zawierał kilka dinozaurów, newArray zawiera kilka obiektów. To kiepskie, prawda?
Matthew James Davis
3
najlepsze podejście, ponieważ utrzymuje funkcje obiektów przy życiu, rathen następnie traci je za pomocą JSON.parse (JSON.stringify (nodesArray))
scipper
14
@MatthewJamesDavis możesz rozwiązać ten problem, zastępując {}go new Dinosaur().
Agargara,
5
płytka kopia nie głęboka kopia
sułtan aslam
1
Działa to świetnie dla szeregu obiektów, jeśli te obiekty zawierają tylko prymitywne właściwości ... to jest to, czego potrzebowałem, dzięki
mojave
154

Jeśli wszystko, czego potrzebujesz, to płytka kopia, naprawdę łatwym sposobem jest:

new_array = old_array.slice(0);
Leopd
źródło
6
Nie sądzę, że musisz zdać 0, ale i tak możesz zadzwonić .slice()przynajmniej w chromie
slf
112
To tak naprawdę nie działa, prawda? To znaczy, to nie jest odpowiedź na pytanie, jak sklonować tablicę obiektów. Jest to rozwiązanie do klonowania prostej tablicy.
bozdoz
35
W rzeczywistości nie zadziała to dla tablicy obiektów. Zwrócona tablica autorstwa slicebędzie nową tablicą, ale będzie zawierać odniesienia do oryginalnych obiektów tablicy.
Sergio A.,
4
Działa to tylko dla „ogólnych” int, ciągów itp., Ale nie dla tablicy obiektów.
Stefan Michev
5
dla tablicy obiektów, która tak naprawdę nie klonuje, aktualizacja do nowej_tablicy spowoduje również aktualizację starej_tablicy.
Anas
44

Najlepszy i najbardziej aktualny sposób wykonania tego klonu jest następujący:

Korzystanie z ...operatora rozprzestrzeniania ES6.

Oto najprostszy przykład:

var clonedObjArray = [...oldObjArray];

W ten sposób rozkładamy tablicę na poszczególne wartości i umieszczamy ją w nowej tablicy za pomocą operatora [].

Oto dłuższy przykład pokazujący różne sposoby jego działania:

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

MennyMez
źródło
2
Dobra nowoczesna odpowiedź, która nie będzie działać ze starszymi przeglądarkami (jak IE 11)
Jealie
1
@Jealie, zgaduję, że KingpinEX celuje w tę odpowiedź dla osób transponujących es6 na coś bardziej uniwersalnie przydatnego z Babelem lub czymś innym.
ruffin
61
To tylko głębokie kopiowanie tablicy, a nie każdego obiektu w tablicy.
Toivo Säwén
31
Kontynuacja wypowiedzi @ ToivoSäwén nie spowoduje głębokiego skopiowania obiektów w tablicy. Nadal będzie odwoływał się do oryginalnych obiektów, więc jeśli je zmutujesz, wpłynie to również na oryginalną tablicę.
Joel Kinzel
3
Działa tylko dla prymitywów. Spróbuj tego: objArray [0] .a = 3; i zobaczysz, że odwołanie do obiektu pozostaje takie samo w cllonArray.
Sergio Correa,
25

To działa dla mnie:

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

A jeśli potrzebujesz głębokiej kopii obiektów w tablicy:

var clonedArray = $.map(originalArray, function (obj) {
                      return $.extend(true, {}, obj);
                  });
wilki
źródło
1
Wygląda na to, że to zadziała. Staram się unikać szerokiego użycia jQuery, więc nie będę go używał w mojej sytuacji, ale pętla for i for ... działałyby.
bozdoz
19
$.evalJSON($.toJSON(origArray));
w przeciwnym razie
źródło
2
Aby skorzystać z tego code.google.com/p/jquery-json,
musisz
32
Bez JQ (dobrze w nowoczesnych przeglądarkach):JSON.parse(JSON.stringify(origArray));
forresto
Uważam ten komentarz za użyteczny. W mojej implementacji musiałem wykonać kopię tablicy obiektów z zastosowanymi właściwościami obserwowalnymi KnockoutJS. Kopia potrzebowała tylko wartości, a nie obserwowalności. Aby wykonać kopię JUST wartości użyłem JSON.parse (ko.toJSON (origArray)) LUB ko.utils.parseJson (ko.toJSON (origArray)). Tylko moje 2 centy i dziękuję za pomoc w osiągnięciu mojego rozwiązania.
wavedrop,
6
JSON.parse(JSON.stringify(origArray));jest zdecydowanie najprostszym rozwiązaniem.
yorkw
jQuery jest często niepotrzebny. youmightnotneedjquery.com
ADJenks
9

Mapa utworzy nową tablicę ze starego (bez odniesienia do starej), a wewnątrz mapy utworzysz nowy obiekt i przejdziesz przez właściwości (klucze) i przypiszesz wartości ze starego obiektu Array do odpowiednich właściwości do nowego obiektu.

Spowoduje to utworzenie dokładnie tej samej tablicy obiektów.

let newArray = oldArray.map(a => {
               let newObject = {};
               Object.keys(a).forEach(propertyKey => {
                    newObject[propertyKey] = a[propertyKey];
               });
               return newObject ;
});
eomeroff
źródło
8

Mogę mieć prosty sposób na zrobienie tego bez konieczności bolesnej rekurencji i nie znając wszystkich drobniejszych szczegółów przedmiotowego obiektu. Korzystając z jQuery, po prostu przekonwertuj swój obiekt na JSON za pomocą jQuery $.toJSON(myObjectArray), a następnie weź łańcuch JSON i oceń go z powrotem na obiekt. BAM! Gotowe i gotowe! Problem rozwiązany. :)

var oldObjArray = [{ Something: 'blah', Cool: true }];
var newObjArray = eval($.toJSON(oldObjArray));
Jerzy
źródło
21
Niektóre nowoczesne przeglądarki mają wbudowaną metodę JSON, więc możesz to zrobić: JSON.parse (JSON.stringify (MY_ARRAY)), która powinna być szybsza. Dobry pomysł.
Nicolas R
1
A jeśli nie używają json2 , nie eval.
kamranicus
To ma okropny występ, ale niestety jest najlepszą odpowiedzią, jaką widziałem: /
Dvid Silva
Nie ewaluuj niczego z danymi użytkownika. Najlepiej nigdy nie używaj eval()w ogóle. To ryzyko bezpieczeństwa.
ADJenks
8

Odpowiadam na to pytanie, ponieważ wydaje się, że nie istnieje proste i jednoznaczne rozwiązanie problemu „klonowania tablicy obiektów w 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

To rozwiązanie iteruje wartości tablic, a następnie iteruje klucze obiektów, zapisując je do nowego obiektu, a następnie wypychając ten nowy obiekt do nowej tablicy.

Zobacz jsfiddle . Uwaga: proste .slice()lub [].concat()niewystarczające dla obiektów w tablicy.

bozdoz
źródło
dzięki za odpowiedź, ale powinieneś zwrócić uwagę na niedociągnięcia w odpowiedzi. Nie działa, gdy w obiektach są obiekty… prawda?
Surowy
utworzy płytką kopię. nie głęboko
sułtan aslam
musisz gdzieś dodać rekurencję
DGoiko,
6

Rozszerzenie JQuery działa dobrze, wystarczy określić, że klonujesz tablicę zamiast obiektu ( zwróć uwagę na [] zamiast {} jako parametr metody ext ):

var clonedNodesArray = jQuery.extend([], nodesArray);
Stef
źródło
2
Hmm, jeśli głosujesz za tym, czy możesz dodać komentarz na temat tego, dlaczego to robisz? Czy możesz najpierw wypróbować kod i sprawdzić, czy działa, czy nie? Dzięki;)
Stef
1
Po zmianie obiektu w pierwszej tablicy obiekt w drugiej tablicy zostaje zmodyfikowany, więc nie jest w porządku.
Spikolynn
6

Ta metoda jest bardzo prosta i możesz modyfikować swój klon bez modyfikowania oryginalnej tablicy.

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

Rafael Grilli
źródło
1
To robi płytkie kopia to dwa poziomy głębokości, natomiast [...oldArray]i oldArray.slice(0)zrobić płytkim skopiować jeden głębokim poziomie. Jest to więc bardzo przydatne, ale nie rzeczywisty pełny głęboki klon.
Ben Wheeler
Prawdziwego głębokiego klonowania można dokonać przy użyciu lodash.clonedeepnpm
odezwano
5

Jak wspomniał Daniel Lew, wykresy cykliczne mają pewne problemy. Gdybym miał ten problem, dodałbym specjalne clone()metody do problematycznych obiektów lub pamiętam, które obiekty już skopiowałem.

Zrobiłbym to ze zmienną, copyCountktóra zwiększa się o 1 za każdym razem, gdy kopiujesz swój kod. Obiekt, który ma niższy copyCountniż bieżący proces kopiowania, jest kopiowany. Jeśli nie, należy powołać się na kopię, która już istnieje. Dlatego konieczne jest połączenie z oryginału do jego kopii.

Nadal jest jeden problem: pamięć. Jeśli masz to odniesienie od jednego obiektu do drugiego, prawdopodobnie przeglądarka nie może zwolnić tych obiektów, ponieważ zawsze są one skądś odwoływane. Będziesz musiał wykonać drugie przejście, w którym ustawisz wszystkie odwołania do kopii na Null. (Jeśli to zrobisz, nie będziesz musiał miećcopyCount logicznej, ale wystarczy boolean isCopied, ponieważ możesz zresetować wartość w drugim przebiegu).

Georg Schölly
źródło
4

Tablicy.slice można użyć do skopiowania tablicy lub jej części. Http://www.devguru.com/Technologies/Ecmascript/Quickref/Slice.html To działałoby z ciągami i liczbami. - zmiana ciągu w jedna tablica nie wpłynie na drugą - ale obiekty są nadal tylko kopiowane przez referencję, więc zmiany w obiektach referencyjnych w jednej tablicy miałyby wpływ na drugą tablicę.

Oto przykład menedżera cofania JavaScript, który może być przydatny w tym przypadku: http://www.ridgway.co.za/archive/2007/11/07/simple-javascript-undo-manager-for-dtos.aspx

markt
źródło
Wiem. Powodem, dla którego chciałem to zaimplementować, jest to, że próbuję rozwiązać problem CSP z cofaniem się. Pomyślałem, że jednym ze sposobów implementacji śledzenia wstecznego może być „robienie migawek” stan przypisania zmiennych przez ... klonowanie takich migawek do stosu.
wallyqs
... i cóż, to może być bardzo zły pomysł.
wallyqs
Takie podejście może mieć inne komplikacje związane z synchronizacją :) .. Skąd wiesz, że tablica nie jest zmieniana podczas robienia zdjęcia?
markt
Dodano link do artykułu, w którym autor zaimplementował prosty menedżer cofania za pomocą javascript ..
markt
4

Moje podejście:

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

daje mi ładny, czysty, głęboki klon oryginalnej tablicy - żaden obiekt nie odwołuje się do oryginału :-)

mgławice
źródło
To najlepsze rozwiązanie z wykorzystaniem jquery. krótkie i słodkie.
John Henckel
1
Zrobiłem test wydajności i wydaje się, że to rozwiązanie jest około dwa razy szybsze niż rozwiązanie JSON.stringify.
meehocz
4

lodash ma cloneDeepdo tego celu funkcję:

var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
DicBrus
źródło
4

Jeśli chcesz wdrożyć głęboki klon, użyj JSON.parse (JSON.stringify (twój {} lub []))

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

sudheer nunna
źródło
3

zapomnij eval () (jest najczęściej wykorzystywaną funkcją JS i powoduje spowolnienie kodu) i slice (0) (działa tylko dla prostych typów danych)

To dla mnie najlepsze rozwiązanie:

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;
};
krupar
źródło
3

Byłem bardzo sfrustrowany tym problemem. Najwyraźniej problem powstaje, gdy wyślesz ogólną tablicę do metody $ .extend. Tak więc, aby to naprawić, dodałem mały czek i działa idealnie z tablicami ogólnymi, tablicami jQuery i dowolnymi obiektami.

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

Wywołaj używając:

var arrNewArrayClone = jQuery.deepclone(arrOriginalArray);
// Or more simply/commonly
var arrNewArrayClone = $.deepclone(arrOriginalArray);
Brak
źródło
deepclone? Używam jquery-1.9.1 i nie obsługuje tej metody. Czy to metoda nowocześniejszej wersji?
user5260143,
@ user2783091 rozszerza JQuery, aby dodać tę funkcję. To nie jest coś, co wychodzi z pudełka
JorgeeFG
3

To głęboko kopiuje tablice, obiekty, wartości zerowe i inne wartości skalarne, a także głęboko kopiuje wszelkie właściwości funkcji nienatywnych (co jest dość rzadkie, ale możliwe). (W celu zwiększenia wydajności nie próbujemy kopiować właściwości nienumerycznych do tablic).

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;
}
Brett Zamir
źródło
3

Korzystam z nowej metody ECMAScript 6 Object.assign :

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

pierwszym argumentem tej metody jest tablica do aktualizacji, przekazujemy pusty obiekt, ponieważ chcemy mieć nowy obiekt.

możemy również użyć tej składni, która jest taka sama, ale krótsza:

let newObject = [...oldObject];
Chtiwi Malek
źródło
Należy pamiętać, że te podejścia będą kopiować tylko odwołania do tablic i obiektów w tablicy, i nie będą tworzyć nowych kopii tych. Spodziewaj się, że to pęknie w przypadku struktur wielowymiarowych.
Ben Wheeler
2

Możemy wynaleźć prostą metodę macierzy rekurencyjnej do klonowania tablic wielowymiarowych. Podczas gdy obiekty w zagnieżdżonych tablicach zachowują odniesienie do odpowiednich obiektów w tablicy źródłowej, tablice tego nie zrobią.

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

Redu
źródło
2

W JavaScript, tablica i kopia obiektu zmieniają wartości początkowe, więc rozwiązaniem jest kopiowanie głębokie.

Głęboka kopia oznacza tworzenie nowej tablicy i kopiowanie wartości, ponieważ cokolwiek się z nią stanie, nigdy nie wpłynie na pierwotną.

JSON.parsei JSON.stringifyjest najlepszym i prostym sposobem na głębokie kopiowanie. JSON.stringify()Metoda konwertuje wartość JavaScript do JSON string.TheJSON.parse() metody analizuje ciąg JSON, konstruowanie wartości JavaScript lub obiekt opisany przez ciąg.

// Deep Clone

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

Aby uzyskać więcej informacji: Przeczytaj tutaj

Bijay Rai
źródło
1
To najlepsze rozwiązanie. Dziękuję Ci.
Nikolay
1

z jQuery:

var target= [];
$.each(source, function() {target.push( $.extend({},this));});
lujan99
źródło
1

Poniższy kod wykona rekurencyjnie głębokie kopiowanie obiektów i tablicy :

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

Źródło

Franck Dernoncourt
źródło
arguments.calleenie jest dostępny w trybie ścisłym i w przeciwnym razie ma problemy z wydajnością.
Brett Zamir
0

Wydaje mi się, że udało mi się napisać ogólną metodę głębokiego klonowania dowolnej struktury JavaScript, która głównie Object.createjest obsługiwana we wszystkich nowoczesnych przeglądarkach. Kod jest taki:

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;
}
ozantunca
źródło
Object.createbędzie traktowany itemjako prototyp obiektu, ale różni się od klonowania. Jeśli itemzostanie zmodyfikowany, zmiany zostaną odzwierciedlone w jego „klonie” i odwrotnie. To podejście nie działa.
Brett Zamir
0

Również do klonowania obiektów zamierzałem po prostu zasugerować ECMAScript 6 reduce():

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

Ale szczerze, jeszcze bardziej podoba mi się odpowiedź @dinodsaurus. Po prostu umieszczam tę wersję tutaj jako inną opcję, ale osobiście będę jej używać map()zgodnie z sugestią @dinodsaurus.

Garret Wilson
źródło
0
       var game_popularity = [
            { game: "fruit ninja", popularity: 78 },
            { game: "road runner", popularity: 20 },
            { game: "maze runner", popularity: 40 },
            { game: "ludo", popularity: 75 },
            { game: "temple runner", popularity: 86 }
        ];
        console.log("sorted original array before clonning");
        game_popularity.sort((a, b) => a.popularity < b.popularity);
        console.log(game_popularity);


        console.log("clone using object assign");
        const cl2 = game_popularity.map(a => Object.assign({}, a));
        cl2[1].game = "clash of titan";
        cl2.push({ game: "logan", popularity: 57 });
        console.log(cl2);


        //adding new array element doesnt reflect in original array
        console.log("clone using concat");
        var ph = []
        var cl = ph.concat(game_popularity);

        //copied by reference ?
        cl[0].game = "rise of civilization";

        game_popularity[0].game = 'ping me';
        cl.push({ game: "angry bird", popularity: 67 });
        console.log(cl);

        console.log("clone using ellipses");
        var cl3 = [...game_popularity];
        cl3.push({ game: "blue whale", popularity: 67 });
        cl3[2].game = "harry potter";
        console.log(cl3);

        console.log("clone using json.parse");
        var cl4 = JSON.parse(JSON.stringify(game_popularity));
        cl4.push({ game: "home alone", popularity: 87 });
        cl4[3].game ="lockhead martin";
        console.log(cl4);

        console.log("clone using Object.create");
        var cl5 = Array.from(Object.create(game_popularity));
        cl5.push({ game: "fish ville", popularity: 87 });
        cl5[3].game ="veto power";
        console.log(cl5);


        //array function
        console.log("sorted original array after clonning");
        game_popularity.sort((a, b) => a.popularity < b.popularity);
        console.log(game_popularity);


        console.log("Object.assign deep clone object array");
        console.log("json.parse deep clone object array");
        console.log("concat does not deep clone object array");
        console.log("ellipses does not deep clone object array");
        console.log("Object.create does not deep clone object array");


        Output:


        sorted original array before clonning
        [ { game: 'temple runner', popularity: 86 },
        { game: 'fruit ninja', popularity: 78 },
        { game: 'ludo', popularity: 75 },
        { game: 'maze runner', popularity: 40 },
        { game: 'road runner', popularity: 20 } ]
        clone using object assign
        [ { game: 'temple runner', popularity: 86 },
        { game: 'clash of titan', popularity: 78 },
        { game: 'ludo', popularity: 75 },
        { game: 'maze runner', popularity: 40 },
        { game: 'road runner', popularity: 20 },
        { game: 'logan', popularity: 57 } ]
        clone using concat
        [ { game: 'ping me', popularity: 86 },
        { game: 'fruit ninja', popularity: 78 },
        { game: 'ludo', popularity: 75 },
        { game: 'maze runner', popularity: 40 },
        { game: 'road runner', popularity: 20 },
        { game: 'angry bird', popularity: 67 } ]
        clone using ellipses
        [ { game: 'ping me', popularity: 86 },
        { game: 'fruit ninja', popularity: 78 },
        { game: 'harry potter', popularity: 75 },
        { game: 'maze runner', popularity: 40 },
        { game: 'road runner', popularity: 20 },
        { game: 'blue whale', popularity: 67 } ]
        clone using json.parse
        [ { game: 'ping me', popularity: 86 },
        { game: 'fruit ninja', popularity: 78 },
        { game: 'harry potter', popularity: 75 },
        { game: 'lockhead martin', popularity: 40 },
        { game: 'road runner', popularity: 20 },
        { game: 'home alone', popularity: 87 } ]
        clone using Object.create
        [ { game: 'ping me', popularity: 86 },
        { game: 'fruit ninja', popularity: 78 },
        { game: 'harry potter', popularity: 75 },
        { game: 'veto power', popularity: 40 },
        { game: 'road runner', popularity: 20 },
        { game: 'fish ville', popularity: 87 } ]
        sorted original array after clonning
        [ { game: 'ping me', popularity: 86 },
        { game: 'fruit ninja', popularity: 78 },
        { game: 'harry potter', popularity: 75 },
        { game: 'veto power', popularity: 40 },
        { game: 'road runner', popularity: 20 } ]

        Object.assign deep clone object array
        json.parse deep clone object array
        concat does not deep clone object array
        ellipses does not deep clone object array
        Object.create does not deep clone object array
sangram
źródło