W3docs

JavaScript-Objektreferenzen und Kopieren

Wie JavaScript-Objekte per Referenz gespeichert und kopiert werden, wie man mit Object.assign und Spread klont und wie man sicher mit structuredClone() tief kopiert.

Einer der wichtigsten Unterschiede in JavaScript ist der Umgang mit primitiven Werten im Vergleich zu objects. Primitive werden „als vollständiger Wert" kopiert, aber objects werden „per Referenz" gespeichert und kopiert. Das Missverständnis dieser einen Regel ist die Ursache unzähliger Fehler – deshalb erklärt dieser Leitfaden genau, was im Speicher passiert, wie man objects vergleicht und klont und wie man sie sicher kopiert.

Primitive werden per Wert kopiert

Ein primitiver Wert – ein string, eine Zahl, ein boolean, null, undefined, bigint oder ein Symbol – wird als vollständiger, unabhängiger Wert kopiert. Das Zuweisen einer Variablen an eine andere dupliziert den Wert, sodass die beiden Variablen danach völlig unabhängig sind.

let message = "Hello";
let phrase = message; // a full copy of the string is made

phrase = "Goodbye";

console.log(message); // "Hello"  — unaffected
console.log(phrase);  // "Goodbye"

Das Ändern von phrase hat keinen Einfluss auf message. Es befinden sich zwei unabhängige strings im Speicher.

Objects werden per Referenz gespeichert und kopiert

Objects funktionieren anders. Eine Variable, die einem object zugewiesen ist, enthält nicht das object selbst – sie enthält eine Referenz (einen Zeiger) auf den Speicherort des objects. Das Kopieren dieser Variable kopiert die Referenz, nicht das object. Beide Variablen zeigen dann auf dasselbe object.

javascript— editable

Es gibt immer noch nur ein object. Wir haben lediglich zwei Variablen – user und admin –, die beide auf dieses object verweisen. Eine Änderung, die über eine der beiden vorgenommen wird, ist auch über die andere sichtbar, da sie dasselbe Ding beschreiben.

Vergleich per Referenz

Zwei object-Variablen sind mit == oder === nur dann gleich, wenn sie auf dasselbe object verweisen. Zwei unabhängige objects sind niemals gleich, selbst wenn ihr Inhalt identisch aussieht.

javascript— editable

Das ist beabsichtigt: === für objects fragt „sind das dasselbe object?", nicht „haben diese objects dieselben Daten?". Der Vergleich von Inhalten erfordert einen anderen Ansatz (häufig das Vergleichen von Eigenschaften einzeln oder die Serialisierung mit JSON).

const-Objects können dennoch verändert werden

Eine häufige Überraschung: Ein mit const deklariertes object kann trotzdem seine Eigenschaften geändert bekommen. const sperrt die Bindung – die Variable kann niemals einem anderen Wert zugewiesen werden – aber es sperrt nicht den Inhalt des objects.

const user = { name: 'John' };

user.name = 'Pete'; // OK — we are mutating the object, not reassigning the variable
console.log(user.name); // 'Pete'

// user = { name: 'Alice' }; // TypeError: Assignment to constant variable

Die erste Änderung funktioniert, weil user immer noch auf dasselbe object verweist. Die Neuzuweisung schlägt fehl, weil sie user auf ein brandneues object zeigen lassen würde, was const verbietet. (Um auch den Inhalt einzufrieren, verwende Object.freeze().)

Flaches Klonen

Was, wenn man wirklich eine separate Kopie eines objects möchte? Man muss ein neues object erstellen und die vorhandenen Eigenschaften hineinkopieren. Zwei moderne, kompakte Wege dazu sind Object.assign und die Spread-Syntax.

Object.assign(target, ...sources) kopiert alle aufzählbaren eigenen Eigenschaften der Quellen in das Ziel und gibt das Ziel zurück:

javascript— editable

Die Spread-Syntax {...obj} macht dasselbe in noch kürzerer Form (siehe Rest-Parameter und Spread-Syntax):

let user = { name: 'John', age: 30 };

let clone = { ...user };       // a shallow copy
let merged = { ...user, age: 31 }; // copy, then override age

console.log(clone);  // { name: 'John', age: 30 }
console.log(merged); // { name: 'John', age: 31 }

Beide Techniken erzeugen eine echte, unabhängige Kopie – aber nur auf der obersten Ebene. Dieser Vorbehalt ist wichtig und führt zum nächsten Abschnitt.

Das Problem mit verschachtelten Objects

Eine flache Kopie dupliziert die Eigenschaften der obersten Ebene. Wenn der Wert einer Eigenschaft jedoch selbst ein object oder array ist, wird nur die Referenz auf dieses verschachtelte object kopiert – nicht das verschachtelte object selbst. Der Klon und das Original teilen sich daher dasselbe verschachtelte object.

javascript— editable

Obwohl clone ein eigenständiges object auf der obersten Ebene ist, zeigen clone.sizes und user.sizes auf ein und dasselbe verschachtelte object. Eine Änderung über einen der beiden Pfade wirkt sich auf beide aus. Das ist genau die Art von versehentlichem Shared-Reference-Fehler, der Menschen trifft, die davon ausgehen, dass eine Spread-Kopie „vollständig unabhängig" ist.

Warnung

Spread ({...obj}) und Object.assign kopieren nur eine Ebene tief. Wenn dein object verschachtelte objects oder arrays enthält, teilt die Kopie diese verschachtelten Werte mit dem Original – das Ändern einer verschachtelten Eigenschaft über eine ändert stillschweigend auch die andere. Für verschachtelte Daten sollte man einen tiefen Klon verwenden.

Tiefes Klonen mit structuredClone()

Um ein object und alles darin Verschachtelte zu kopieren, verwende das eingebaute structuredClone(). Es klont verschachtelte objects und arrays rekursiv, sodass das Ergebnis vollständig unabhängig vom Original ist.

javascript— editable

structuredClone verarbeitet auch zirkuläre Referenzen (ein object, das direkt oder indirekt auf sich selbst verweist), ohne abzustürzen:

let user = {};
user.self = user; // user references itself

let clone = structuredClone(user);

console.log(clone === clone.self); // true — the cycle is preserved correctly

Über einfache objects und arrays hinaus unterstützt es viele eingebaute Typen, darunter Date, Map, Set, RegExp, ArrayBuffer und typisierte arrays – ihre Werte werden originalgetreu kopiert, nicht in etwas anderes umgewandelt.

Einschränkungen von structuredClone

structuredClone ist leistungsfähig, aber nicht universell:

  • Es kann keine Funktionen klonen – der Versuch wirft einen DataCloneError. Dasselbe gilt für DOM-Knoten.
  • Es kopiert nicht Symbol-Schlüssel-Eigenschaften, Getter/Setter von Eigenschaften oder die Prototypkette des objects – diese werden einfach aus dem Ergebnis entfernt.
// This throws DataCloneError because functions can't be cloned:
// structuredClone({ run: () => {} });

Wenn man Funktionen oder Klasseninstanzen mit ihren Methoden kopieren muss, ist structuredClone nicht das richtige Werkzeug – man würde einen eigenen Klon schreiben oder eine Bibliothek wie lodash's cloneDeep verwenden.

Der alte JSON-Trick

Bevor structuredClone weitgehend verfügbar war, war ein beliebter Tiefen-Klon-Hack das Serialisieren eines objects in einen JSON-string und das anschließende Parsen:

let user = { name: 'John', sizes: { height: 182, width: 50 } };

let clone = JSON.parse(JSON.stringify(user)); // deep copy via JSON

clone.sizes.width = 60;
console.log(user.sizes.width); // 50 — original unaffected

Es funktioniert für einfache, JSON-sichere Daten, hat aber echte Tücken:

  • Funktionen und undefined werden verworfen – Schlüssel, die sie enthalten, verschwinden einfach.
  • Date-objects werden zu stringsJSON.stringify wandelt ein Datum in einen ISO-string um, und das Parsen wandelt es nicht zurück.
  • Map, Set und andere Sondertypen gehen verloren oder werden zu leeren objects.
  • Zirkuläre Referenzen werfen einen TypeError.
Info

Bevorzuge für tiefes Klonen structuredClone() gegenüber dem JSON.parse(JSON.stringify(...)) -Trick. Der JSON-Ansatz verliert stillschweigend Funktionen, undefined und Sondertypen, verstümmelt Daten und stürzt bei zirkulären Referenzen ab – structuredClone behandelt all das korrekt.

Ein Hinweis zur Garbage Collection

Sobald man beginnt, Referenzen herumzukopieren, lohnt es sich zu bedenken, wie JavaScript Speicher zurückgewinnt. Ein object bleibt im Speicher, solange es erreichbar ist – solange eine Variable, Eigenschaft oder ein array-Eintrag noch auf es verweist. Wenn die letzte Referenz auf ein object verschwindet, wird es unerreichbar und kommt für die Garbage Collection in Frage. Man gibt objects nie manuell frei; die Engine erledigt das automatisch.

Praxisbeispiel: State vor der Mutation kopieren

Das ist nicht akademisch. In modernem UI-Code (React, Redux und Ähnlichem) lautet die Standardregel: „State nie direkt mutieren – eine neue Kopie mit der angewendeten Änderung erstellen". Flaches Kopieren mit Spread ist das alltägliche Werkzeug dafür:

javascript— editable

Man beachte, dass wir sowohl das object der obersten Ebene als auch das verschachtelte cart-array spreaden. Da Spread flach ist, muss jede verschachtelte Ebene, die man ändern möchte, explizit kopiert werden – andernfalls gerät man direkt wieder in die Shared-Reference-Falle, die zuvor beschrieben wurde. Wenn die Daten tief verschachtelt sind und man eine vollständige, sichere Kopie benötigt, greife auf structuredClone zurück.

Zusammenfassung

  • Primitive werden per Wert kopiert – Kopien sind vollständig unabhängig.
  • Objects werden per Referenz gespeichert und kopiert – das Kopieren der Variable kopiert den Zeiger, sodass beide Variablen dasselbe object teilen.
  • === vergleicht objects per Referenz: nur dasselbe object ist sich selbst gleich.
  • const fixiert die Bindung, nicht den Inhalt – const-Objects können dennoch mutiert werden.
  • Object.assign({}, obj) und {...obj} erstellen flache Kopien, die verschachtelte objects teilen.
  • structuredClone(obj) erstellt eine tiefe Kopie, verarbeitet zirkuläre Referenzen und viele eingebaute Typen, kann aber keine Funktionen oder DOM-Knoten klonen und entfernt Symbol-Schlüssel, Getter und Prototypen.
  • Bevorzuge structuredClone gegenüber dem verlustbehafteten JSON.parse(JSON.stringify(...)) -Trick.

Teste dein Wissen

Übung
Nach let a = {}; let b = a; b.x = 1; — was ist a.x?
Nach let a = {}; let b = a; b.x = 1; — was ist a.x?
Übung
Gegeben let c = { n: 1 }; let d = { n: 1 }; — was ergibt c === d?
Gegeben let c = { n: 1 }; let d = { n: 1 }; — was ergibt c === d?
Übung
Welche Aussage über das Kopieren von objects in JavaScript ist korrekt?
Welche Aussage über das Kopieren von objects in JavaScript ist korrekt?
Was this page helpful?