3750 Stimmen

Wie kann ich ein JavaScript-Objekt korrekt klonen?

Ich habe ein Objekt x . Ich würde es gerne als Objekt kopieren y , so dass Änderungen an y nicht verändern x . Ich habe festgestellt, dass das Kopieren von Objekten, die von integrierten JavaScript-Objekten abgeleitet sind, zu zusätzlichen, unerwünschten Eigenschaften führt. Das ist kein Problem, da ich eines meiner eigenen, wörtlich konstruierten Objekte kopiere.

Wie kann ich ein JavaScript-Objekt korrekt klonen?

35 Stimmen

0 Stimmen

Unterstützen Sie auf jeden Fall @Niyaz! Shortlink: tinyurl.com/JSCopyObject

290 Stimmen

Für JSON verwende ich mObj=JSON.parse(JSON.stringify(jsonObject));

1976voto

A. Levy Punkte 26674

Aktualisierung 2022

Es gibt einen neuen JS-Standard namens "strukturiertes Klonen". Er funktioniert in allen Browsern:

const clone = structuredClone(object);

Alte Antwort

Dies für ein beliebiges Objekt in JavaScript zu tun, ist weder einfach noch unkompliziert. Sie werden auf das Problem stoßen, dass Sie fälschlicherweise Attribute aus dem Prototyp des Objekts übernehmen, die im Prototyp belassen und nicht in die neue Instanz kopiert werden sollten. Wenn Sie z. B. ein clone Methode zu Object.prototype wie in einigen Antworten beschrieben, müssen Sie dieses Attribut explizit auslassen. Aber was ist, wenn es noch andere zusätzliche Methoden gibt, die zu Object.prototype oder andere Zwischenprototypen, von denen Sie nichts wissen? In diesem Fall werden Sie Attribute kopieren, die Sie nicht kopieren sollten, daher müssen Sie unvorhergesehene, nicht-lokale Attribute mit der hasOwnProperty méthode.

Zusätzlich zu den nicht aufzählbaren Attributen werden Sie auf ein schwierigeres Problem stoßen, wenn Sie versuchen, Objekte zu kopieren, die versteckte Eigenschaften haben. Zum Beispiel, prototype ist eine verborgene Eigenschaft einer Funktion. Außerdem wird der Prototyp eines Objekts mit dem Attribut __proto__ die ebenfalls ausgeblendet ist und von einer for/in-Schleife, die über die Attribute des Quellobjekts iteriert, nicht kopiert wird. Ich denke __proto__ kann spezifisch für den JavaScript-Interpreter von Firefox sein, und in anderen Browsern kann es etwas anderes sein, aber Sie verstehen das Bild. Nicht alles ist aufzählbar. Sie können ein verstecktes Attribut kopieren, wenn Sie seinen Namen kennen, aber ich kenne keine Möglichkeit, es automatisch zu entdecken.

Ein weiterer Stolperstein auf der Suche nach einer eleganten Lösung ist das Problem der korrekten Einrichtung der Prototyp-Vererbung. Wenn der Prototyp Ihres Quellobjekts Object und dann einfach ein neues allgemeines Objekt mit {} wird funktionieren, aber wenn der Prototyp der Quelle ein Nachkomme von Object dann fehlen Ihnen die zusätzlichen Mitglieder des Prototyps, die Sie mit der Option hasOwnProperty Filter, oder die im Prototyp enthalten waren, aber von vornherein nicht aufzählbar waren. Eine Lösung könnte darin bestehen, das Quellobjekt aufzurufen constructor Eigenschaft verwenden, um das anfängliche Kopierobjekt zu erhalten und dann die Attribute zu kopieren, aber dann erhalten Sie immer noch keine nicht aufzählbaren Attribute. Zum Beispiel, ein Date Objekt speichert seine Daten als verborgenes Mitglied:

function clone(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    var copy = obj.constructor();
    for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
    }
    return copy;
}

var d1 = new Date();

/* Executes function after 5 seconds. */
setTimeout(function(){
    var d2 = clone(d1);
    alert("d1 = " + d1.toString() + "\nd2 = " + d2.toString());
}, 5000);

Die Datumszeichenfolge für d1 liegt 5 Sekunden hinter dem von d2 . Eine Möglichkeit zur Herstellung einer Date dasselbe wie ein anderer ist, indem man die setTime Methode, aber das ist spezifisch für die Date Klasse. Ich glaube nicht, dass es eine allgemein gültige Lösung für dieses Problem gibt, aber ich würde mich freuen, wenn ich falsch liege!

Als ich das allgemeine tiefe Kopieren implementieren musste, ging ich schließlich einen Kompromiss ein, indem ich annahm, dass ich nur eine einfache Object , Array , Date , String , Number , oder Boolean . Die letzten 3 Typen sind unveränderlich, so dass ich eine flache Kopie durchführen konnte und mir keine Sorgen machen musste, dass sie sich ändert. Außerdem nahm ich an, dass alle Elemente, die in Object o Array wäre auch einer der 6 einfachen Typen in dieser Liste. Dies kann mit Code wie dem folgenden erreicht werden:

function clone(obj) {
    var copy;

    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = clone(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
}

Die obige Funktion ist für die 6 genannten einfachen Typen geeignet, solange die Daten in den Objekten und Arrays eine Baumstruktur bilden. Das heißt, es gibt nicht mehr als einen Verweis auf dieselben Daten im Objekt. Zum Beispiel:

// This would be cloneable:
var tree = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "right" : null,
    "data"  : 8
};

// This would kind-of work, but you would get 2 copies of the 
// inner node instead of 2 references to the same copy
var directedAcylicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
directedAcyclicGraph["right"] = directedAcyclicGraph["left"];

// Cloning this would cause a stack overflow due to infinite recursion:
var cyclicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
cyclicGraph["right"] = cyclicGraph;

Es kann nicht mit jedem JavaScript-Objekt umgehen, aber es kann für viele Zwecke ausreichen, solange man nicht davon ausgeht, dass es einfach für alles funktioniert, was man ihm hinwirft.

0 Stimmen

Es fehlen Symbolschlüssel und Symbolwerte. Heutzutage ist die Verwendung von Object.getOwnPropertyDescriptors ist besser.

0 Stimmen

structuredClone es nur 75 % weltweit kompatibel

0 Stimmen

In Nodejs, structuredClone(object) funktioniert für Knoten v17.0.0 und später.

1164voto

heinob Punkte 18537

Wenn Sie nicht Date s, Funktionen, undefiniert, regExp oder Infinity innerhalb Ihres Objekts, ist ein sehr einfacher Einzeiler JSON.parse(JSON.stringify(object)) :

const a = {
  string: 'string',
  number: 123,
  bool: false,
  nul: null,
  date: new Date(),  // stringified
  undef: undefined,  // lost
  inf: Infinity,  // forced to 'null'
}
console.log(a);
console.log(typeof a.date);  // Date object
const clone = JSON.parse(JSON.stringify(a));
console.log(clone);
console.log(typeof clone.date);  // result of .toISOString()

Dies funktioniert für alle Arten von Objekten, die Objekte, Arrays, Strings, Booleans und Zahlen enthalten.

Siehe auch diesen Artikel über den strukturierten Klon-Algorithmus von Browsern die beim Senden von Nachrichten an und von einem Arbeiter verwendet wird. Sie enthält auch eine Funktion für Deep Cloning.

1 Stimmen

Manchmal sind die besten Antworten die einfachsten. genial.

0 Stimmen

Hilfreich, aber beim Vergleich von Objekten, die andere Objekte enthält, lief ich durch unerwartetes Verhalten, wenn zwei genau gleiche Objekte nicht als gleich genommen wurden. verwendet JSON.stringify(x) == JSON.stringify(JSON.parse(JSON.stringify(a))), um es zu beheben. Aus irgendeinem Grund funktioniert der Vergleich als Strings perfekt wie erwartet, wenn der Vergleich, konnte nicht übereinstimmen sonst.

0 Stimmen

@AgustinL.Lacuara Sie können komplexe Datentypen in JS nicht vergleichen. a={};b={}; a==b es false . Aber nach a=b wird es true denn es ist nicht nur identisch, sondern es ist derselbe Gegenstand.

840voto

Vitalii Fedorenko Punkte 103468

In ECMAScript 6 gibt es Objekt.zuweisen Methode, die die Werte aller aufzählbaren eigenen Eigenschaften von einem Objekt zum anderen kopiert. Zum Beispiel:

var x = {myProp: "value"};
var y = Object.assign({}, x); 

Aber Achtung dies ist eine oberflächliche Kopie - verschachtelte Objekte werden weiterhin als Referenz kopiert.

815voto

Pascal Punkte 8699

Mit jQuery können Sie oberflächliche Kopie con erweitern. :

var copiedObject = jQuery.extend({}, originalObject)

nachträgliche Änderungen der copiedObject hat keinen Einfluss auf die originalObject und andersherum.

Oder um eine tiefe Kopie :

var copiedObject = jQuery.extend(true, {}, originalObject)

330voto

Tareq Punkte 5050

Per MDN :

  • Wenn Sie eine flache Kopie wünschen, verwenden Sie Object.assign({}, a)
  • Für "tiefe" Kopien verwenden Sie JSON.parse(JSON.stringify(a))

Externe Bibliotheken sind nicht erforderlich, aber Sie müssen prüfen Browser-Kompatibilität zuerst .

0 Stimmen

Das Problem tritt auf, wenn Sie Funktionen in Ihrem Objekt haben JSON.parse(JSON.stringify(a))

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