618 Stimmen

Wie klont man ein Array von Objekten in JavaScript?

...wo jedes Objekt auch Verweise auf andere Objekte innerhalb desselben Arrays hat?

Als ich zum ersten Mal mit diesem Problem konfrontiert wurde, dachte ich nur an etwas wie

var clonedNodesArray = nodesArray.clone()

existieren würde, und suchte nach Informationen darüber, wie man Objekte in JavaScript klont. Ich habe gefunden eine Frage auf Stack Overflow (beantwortet von demselben @JohnResig) und er wies darauf hin, dass man mit jQuery Folgendes tun kann

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

um ein Objekt zu klonen. Ich habe dies jedoch ausprobiert, und es werden nur die Referenzen der Objekte im Array kopiert. Wenn ich also

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

wird der Wert sowohl von nodesArray[0] als auch von clonedNodesArray[0] "grün" sein. Dann habe ich versucht

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

das ein Objekt tief kopiert, aber ich bekomme " zu viel Rekursion " und " Kontrollstapelüberlauf " Nachrichten von beiden Firebug y Oper Libelle beziehungsweise.

Wie würden Sie das machen? Ist das etwas, das gar nicht gemacht werden sollte? Gibt es eine wiederverwendbare Möglichkeit, dies in JavaScript zu tun?

787voto

Erstellen einer tiefen Kopie mit structuredClone

Die moderne Art, ein Array in JavaScript tief zu kopieren, ist die Verwendung von structuredClone :

array2 = structuredClone(array1);

Diese Funktion ist jedoch relativ neu (Chrome 98, Firefox 94) und ist derzeit nur verfügbar für etwa 40 % der Benutzer, so dass es ohne Polyfill noch nicht produktionsreif ist.

Als Alternative können Sie eine der unten aufgeführten, gut unterstützten JSON-basierten Lösungen verwenden.

Erstellen einer tiefen Kopie mit JSON.parse

Eine allgemeine Lösung, die alle möglichen Objekte innerhalb eines Arrays von Objekten berücksichtigt, ist möglicherweise nicht möglich. Das heißt, wenn Ihr Array Objekte enthält die JSON-serialisierbaren Inhalt haben (keine Funktionen, keine Number.POSITIVE_INFINITY usw.) ist eine einfache Möglichkeit zur Vermeidung von Schleifen, die allerdings mit Leistungseinbußen verbunden ist, diese reine Vanilla-Einzellösung.

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

Zusammenfassend lässt sich sagen, dass der Hauptvorteil dieses Ansatzes darin besteht, dass auch der Inhalt des Arrays geklont wird, nicht nur das Array selbst. Die primären Nachteile sind die Beschränkung auf JSON-serialisierbare Inhalte und die ~30-mal langsamere Leistung als die Spread-Methode.

Wenn Sie flache Objekte im Array haben und IE6 akzeptabel ist, ist es besser, den Spread-Operator in Kombination mit dem Array-Operator .map zu verwenden. Für eine zwei Ebenen tiefe Situation (wie das Array im Anhang unten):

clonedArray = nodesArray.map(a => {return {...a}})

Dafür gibt es zwei Gründe: 1) Es ist viel, viel schneller (siehe unten für einen Benchmark-Vergleich) und es erlaubt auch jedes gültige Objekt in Ihrem Array.

*Anhang: Die Quantifizierung der Leistung basiert auf dem millionenfachen Klonen dieses Arrays von Objekten:

 [{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic1.jpg?raw=true', id: '1', isFavorite: false}, {url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic2.jpg?raw=true', id: '2', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic3.jpg?raw=true', id: '3', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic4.jpg?raw=true', id: '4', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic5.jpg?raw=true', id: '5', isFavorite: true},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic6.jpg?raw=true', id: '6', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic7.jpg?raw=true', id: '7', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic8.jpg?raw=true', id: '8', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic9.jpg?raw=true', id: '9', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic10.jpg?raw=true', id: '10', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic11.jpg?raw=true', id: '11', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic12.jpg?raw=true', id: '12', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic13.jpg?raw=true', id: '13', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic14.jpg?raw=true', id: '14', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic15.jpg?raw=true', id: '15', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic16.jpg?raw=true', id: '16', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic17.jpg?raw=true', id: '17', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic18.jpg?raw=true', id: '18', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic19.jpg?raw=true', id: '19', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic20.jpg?raw=true', id: '20', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic21.jpg?raw=true', id: '21', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic22.jpg?raw=true', id: '22', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic23.jpg?raw=true', id: '23', isFavorite: false}]

entweder mit:

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

oder:

clonedArray = nodesArray.map(a => {return {...a}})

Der Map/Spread-Ansatz benötigte 0,000466 ms pro Durchgang und JSON.parse und JSON.stringify 0,014771 ms pro Durchgang.

400voto

dinodsaurus Punkte 4751

Ich habe das Klonen eines Arrays von Objekten mit Objekt.zuweisen

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

oder noch kürzer mit Verbreitungssyntax

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

162voto

Leopd Punkte 39216

Wenn Sie nur eine oberflächliche Kopie benötigen, ist das ein wirklich einfacher Weg:

new_array = old_array.slice(0);

122voto

Dan Lew Punkte 83507

Das Problem mit Ihrer flachen Kopie ist, dass nicht alle Objekte geklont werden. Während die Verweise auf jedes Objekt in jedem Array eindeutig sind, sobald Sie schließlich auf sie zugreifen, sind Sie mit dem gleichen Objekt wie zuvor zu tun. Es ist nichts falsch mit der Art, wie Sie es geklont ... das gleiche Ergebnis würde mit Array.slice() auftreten.

Der Grund, warum Ihre Deep Copy Probleme hat, ist, dass Sie mit zirkulären Objektreferenzen enden. Deep wird so tief gehen, wie es gehen kann, und wenn Sie einen Kreis haben, wird es unendlich weitergehen, bis der Browser ohnmächtig wird.

Wenn die Datenstruktur nicht als gerichteter azyklischer Graph dargestellt werden kann, dann bin ich mir nicht sicher, ob Sie eine universelle Methode für tiefes Klonen finden können. Zyklische Graphen bieten viele knifflige Eckfälle, und da es sich nicht um eine übliche Operation handelt, bezweifle ich, dass irgendjemand eine vollständige Lösung geschrieben hat (wenn es überhaupt möglich ist - vielleicht ist es das nicht! Aber ich habe jetzt keine Zeit, einen strengen Beweis zu schreiben.). Ich habe einige gute Kommentare zu diesem Thema gefunden auf diese Seite .

Wenn Sie eine tiefe Kopie eines Arrays von Objekten mit zirkulären Verweisen benötigen, müssen Sie eine eigene Methode zur Behandlung Ihrer speziellen Datenstruktur programmieren, so dass es sich um einen Klon mit mehreren Durchläufen handelt:

  1. Erstellen Sie in der ersten Runde einen Klon aller Objekte, die nicht auf andere Objekte im Array verweisen. Verfolgen Sie die Ursprünge der einzelnen Objekte.
  2. In der zweiten Runde verbinden Sie die Objekte miteinander.

76voto

MennyMez Punkte 2286

Wenn Sie nur eine flach klonen, ist der beste Weg, diesen Klon wie folgt durchzuführen:

Die Verwendung des ... ES6 Spread-Operator.

Hier ist das einfachste Beispiel:

var clonedObjArray = [...oldObjArray];

Auf diese Weise zerlegen wir das Array in einzelne Werte und fügen sie mit dem Operator [] in ein neues Array ein.

Hier ist ein längeres Beispiel, das die verschiedenen Möglichkeiten aufzeigt, wie es funktioniert:

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X