JS
() =>
JavaScript · Web API · Deep Clone · Datenstrukturen
structuredClone()
Deep Clone endlich nativ – kein JSON-Roundtrip mehr

Der JSON-Roundtrip-Trick ist seit Jahren der Standard für Deep Cloning in JavaScript – und seit Jahren verliert er Date-Objekte, Map, Set, undefined-Werte und zirkuläre Referenzen. structuredClone() ist die native Lösung, die seit 2022 in allen modernen Engines verfügbar ist und all das korrekt behandelt.

13 Min. Lesezeit structuredClone · Structured Clone Algorithm · Transfer · ArrayBuffer Chrome 98+ · Firefox 94+ · Safari 15.4+ · Node.js 17+

1. Das Problem mit JSON.parse(JSON.stringify())

Der Trick JSON.parse(JSON.stringify(obj)) ist in JavaScript-Codebasen allgegenwärtig – und er ist grundlegend gebrochen für jeden Datentyp, der über einfache Strings, Zahlen und verschachtelte Objekte hinausgeht. Date-Objekte werden zu Strings konvertiert und nicht als Date zurückgegeben. Map und Set werden zu leeren Objekten. undefined-Werte verschwinden aus Objekten und werden in Arrays zu null. Infinity und NaN werden zu null. Reguläre Ausdrücke werden zu leeren Objekten. Und zirkuläre Referenzen werfen einen TypeError. Diese Verluste passieren still – es gibt keine Warnung, kein Error, nur ein subtil falsches Ergebnis.

Das Resultat in realen Projekten: Tests, die mit einem Deep Clone-Trick aufgesetzt wurden, passen plötzlich nicht mehr auf die echten Daten. State-Management-Code, der Zustandssnapshots per JSON-Roundtrip nimmt, verliert Datum-Informationen. Worker-Kommunikation, die Objekte serialisiert, zerstört Map-Strukturen. Diese Fehler sind schwer zu debuggen, weil das kopierte Objekt oberflächlich korrekt aussieht. structuredClone() löst all diese Probleme auf einmal und ist seit 2022 in allen modernen JavaScript-Engines ohne Polyfill verfügbar.

2. Was structuredClone() ist und wie es funktioniert

structuredClone() ist eine globale Web-API-Funktion, die den Structured Clone Algorithm implementiert – denselben Algorithmus, den Browser intern für postMessage(), IndexedDB-Writes und die History-API nutzen. Der Algorithmus traversiert das Eingabeobjekt vollständig und erstellt eine echte, tiefe Kopie – jedes Objekt in jedem Verschachtelungsniveau wird als neue Instanz erzeugt. Änderungen am Klon beeinflussen das Original nicht, und Änderungen am Original beeinflussen den Klon nicht.

Die interne Implementierung des Structured Clone Algorithms führt eine Objekt-Graph-Traversierung mit einem Identitätscache durch. Wenn dasselbe Objekt mehrfach in der Struktur referenziert wird, wird es nur einmal geklont und alle Referenzen im Klon zeigen auf dieselbe neue Kopie. Das ist der Mechanismus, der zirkuläre Referenzen korrekt behandelt und verhindert, dass der Algorithmus in Endlosschleifen gerät. Der Identitätscache wird nach dem Klonvorgang verworfen. structuredClone() ist synchron und blockiert den Haupt-Thread – für sehr große Objekte kann das relevant sein.


// structuredClone() — correct deep cloning for real-world data
const original = {
  name: 'Mironsoft Project',
  createdAt: new Date('2026-01-15'),           // Date object
  tags: new Set(['typescript', 'magento']),     // Set
  meta: new Map([['version', '1.0'], ['env', 'prod']]), // Map
  pattern: /^\d{4}-\d{2}-\d{2}$/,             // RegExp
  stats: { views: 1024, undefined: undefined }, // undefined value preserved
  buffer: new ArrayBuffer(16),                  // Binary data
};

// JSON.parse(JSON.stringify(original)) would corrupt all of the above
const broken = JSON.parse(JSON.stringify(original));
console.log(broken.createdAt instanceof Date);  // false — it's a string!
console.log(broken.tags);                        // {} — empty object, not a Set
console.log(broken.pattern);                     // {} — empty object, not RegExp

// structuredClone() handles all of them correctly
const clone = structuredClone(original);
console.log(clone.createdAt instanceof Date);    // true
console.log(clone.tags instanceof Set);          // true
console.log(clone.meta instanceof Map);          // true
console.log(clone.pattern instanceof RegExp);    // true
console.log('undefined' in clone.stats);         // true

// Verify deep independence — modifying clone does not affect original
clone.name = 'Modified';
clone.tags.add('alpine');
console.log(original.name);         // 'Mironsoft Project' — unchanged
console.log(original.tags.size);    // 2 — unchanged

3. Unterstützte Typen: Date, Map, Set, RegExp und mehr

Der Structured Clone Algorithm unterstützt eine explizit definierte Liste von JavaScript-Typen. Primitive Werte (Number, String, Boolean, BigInt, null, undefined) werden kopiert. Date-Objekte werden als echte Date-Kopien mit demselben Timestamp geklont. RegExp-Objekte werden mit demselben Pattern und denselben Flags geklont. Map und Set werden vollständig geklont – inklusive aller Einträge, die selbst wiederum tief geklont werden. ArrayBuffer, SharedArrayBuffer, TypedArrays (Uint8Array, Float64Array etc.) und DataView werden korrekt kopiert.

Neuere Typen wie Blob, File, FileList, ImageData, ImageBitmap und CryptoKey werden ebenfalls vom Structured Clone Algorithm unterstützt. structuredClone() kann also für deutlich mehr Szenarien genutzt werden als nur für Plain-Object-Kloning. Besonders interessant ist die Unterstützung für CryptoKey: Kryptografische Schlüssel können geklont und an Web Worker übergeben werden, ohne sie erneut importieren zu müssen. Nicht unterstützt sind Funktionen, DOM-Elemente, Fehler mit Stack-Traces und Proxy-Objekte – dafür wirft structuredClone() einen DataCloneError.

4. Zirkuläre Referenzen korrekt klonen

Zirkuläre Referenzen sind ein häufiges Muster in Datenstrukturen, die Eltern-Kind-Beziehungen modellieren: Ein Kind-Objekt hat eine Referenz auf sein Eltern-Objekt, das seinerseits das Kind-Objekt in einem Array enthält. Dieser Objektgraph ist mit JSON.stringify() nicht serialisierbar – ein TypeError: Converting circular structure to JSON beendet den Versuch sofort. Auch viele manuelle Recursive-Clone-Implementierungen scheitern an zirkulären Referenzen durch unendliche Rekursion.

structuredClone() behandelt zirkuläre Referenzen korrekt und ohne zusätzliche Konfiguration. Der interne Identitätscache des Structured Clone Algorithms merkt sich alle bereits geklonten Objekte und gibt beim zweiten Antreffen derselben Referenz die gecachte Kopie zurück statt erneut zu klonen. Das Ergebnis ist ein geklonter Objektgraph, der dieselbe Zirkularitätsstruktur wie das Original aufweist – mit dem Unterschied, dass alle Objekte neue Instanzen sind. Das ist das korrekte, erwartete Verhalten für einen tiefen Klon.


// Circular reference handling and the transfer option
// Circular references: JSON.stringify would throw TypeError
const parent = { name: 'Parent', children: [] };
const child = { name: 'Child', parent };
parent.children.push(child);

// JSON.parse(JSON.stringify(parent)) → TypeError: circular structure
// structuredClone handles it correctly
const clonedParent = structuredClone(parent);
console.log(clonedParent.children[0].parent === clonedParent); // true — structure preserved
console.log(clonedParent === parent);                            // false — new instance

// Shared references are preserved correctly
const sharedData = { value: 42 };
const obj = { a: sharedData, b: sharedData };
const cloned = structuredClone(obj);
console.log(cloned.a === cloned.b); // true — same reference within the clone
console.log(cloned.a === sharedData); // false — new instance, not original

// Transfer option: move ArrayBuffer ownership (zero-copy)
// Original buffer becomes detached (empty) after transfer
const bigBuffer = new ArrayBuffer(1024 * 1024); // 1 MB
const view = new Uint8Array(bigBuffer);
view[0] = 255;

const transferred = structuredClone(
  { buffer: bigBuffer },
  { transfer: [bigBuffer] } // transfer ownership, do not copy
);

console.log(bigBuffer.byteLength);          // 0 — detached, no longer accessible
console.log(transferred.buffer.byteLength); // 1048576 — new owner

5. Transfer: Ownership von ArrayBuffer übergeben

structuredClone() unterstützt einen optionalen zweiten Parameter: { transfer: [arrayBuffer1, arrayBuffer2] }. Mit dieser Option werden die angegebenen ArrayBuffer nicht kopiert, sondern deren Ownership übertragen. Das Original wird nach der Übertragung detached – sein byteLength ist 0, und jeder Zugriffsversuch wirft einen TypeError. Das Ziel-Objekt erhält denselben Speicherblock, ohne dass die Daten kopiert wurden. Das ist eine Zero-Copy-Operation.

Der Transfer-Mechanismus ist das, was postMessage() intern für effiziente Worker-Kommunikation nutzt. Wenn ein Web Worker ein 100-MB-ArrayBuffer verarbeiten soll, wäre das Kopieren des gesamten Puffers teuer. Mit Transfer wird die Ownership in Mikrosekunden übertragen, unabhängig von der Puffergröße. structuredClone() mit Transfer-Option macht dasselbe Muster auch für andere Szenarien verfügbar, ohne einen Web Worker oder postMessage() zu benötigen – zum Beispiel für das effiziente Übergeben von Binärdaten zwischen Modulen, wo Immutability des ursprünglichen Puffers gewünscht ist.

6. Was structuredClone() nicht kann: Funktionen und Prototypen

Der wichtigste Unterschied zwischen structuredClone() und Bibliotheken wie Lodash cloneDeep() betrifft Prototypen und Klasseninstanzen. structuredClone() klont nur die Daten eines Objekts, nicht seinen Prototypen. Wenn man eine Klasseninstanz mit eigenen Methoden klont, enthält der Klon dieselben Dateneigenschaften, aber er ist eine Plain-Object-Kopie – er ist keine Instanz der Originalklasse, und seine Methoden sind nicht verfügbar. clone instanceof MyClass gibt false zurück.

Funktionen können generell nicht geklont werden – weder mit dem Structured Clone Algorithm noch auf sinnvolle Weise mit anderen Methoden. structuredClone() wirft für Objekte mit Funktions-Properties einen DataCloneError. Das ist der Hauptgrund, warum cloneDeep() aus Lodash in manchen Szenarien nach wie vor sinnvoll ist: Lodash kopiert Funktionsreferenzen und behält Prototypen erhalten, während structuredClone() das bewusst nicht tut. Die Entscheidung zwischen beiden hängt davon ab, ob man Daten klonen möchte (structuredClone()) oder ob man Klasseninstanzen mit Methoden kopieren muss (Lodash oder manuell).

7. Performance: structuredClone vs. JSON vs. Lodash

Performance-Benchmarks für Deep Cloning sind stark abhängig von der Objektstruktur. Für flache Plain-Objects mit einfachen primitiven Werten ist der JSON-Roundtrip oft schneller als structuredClone(), weil JSON.parse/stringify stark optimiert ist. Aber sobald das Objekt tiefer wird, Maps, Sets oder ArrayBuffers enthält, kehren sich die Vorzeichen um. Der JSON-Roundtrip muss außerdem die String-Serialisierung und -Deserialisierung durchlaufen, was bei großen Objekten speicherintensiver ist als die direkte Objekt-Traversierung von structuredClone().

Lodash cloneDeep() ist eine vollständige JavaScript-Implementierung, die deshalb langsamer als die nativen Implementierungen ist. Moderne JavaScript-Engines implementieren structuredClone() nativ in C++ mit direktem Zugang zu den internen Objektrepräsentationen, was erheblich schneller ist als eine JavaScript-Implementierung derselben Traversierung. Für die meisten realen Anwendungsfälle – Zustandssnapshots, Worker-Kommunikation, Test-Setup – ist die Performance-Differenz zwischen structuredClone() und JSON ohnehin irrelevant. Der entscheidende Faktor ist Korrektheit, und hier ist structuredClone() klar überlegen.


// Practical patterns for structuredClone() in real applications

// Pattern 1: Immutable state snapshots (Redux / Zustand style)
function createStateManager(initialState) {
  let state = structuredClone(initialState);
  const listeners = new Set();

  return {
    getState: () => structuredClone(state), // return a deep copy, not the reference
    setState(updater) {
      const nextState = updater(structuredClone(state));
      state = nextState;
      listeners.forEach((fn) => fn(structuredClone(state)));
    },
    subscribe(fn) {
      listeners.add(fn);
      return () => listeners.delete(fn);
    },
  };
}

// Pattern 2: Test fixture isolation — each test gets its own deep copy
const FIXTURE_USER = {
  id: 1,
  name: 'Test User',
  roles: new Set(['editor', 'viewer']),
  createdAt: new Date('2026-01-01'),
  meta: new Map([['plan', 'pro']]),
};

function getTestUser() {
  return structuredClone(FIXTURE_USER); // isolated copy for each test
}

// Pattern 3: Undo/Redo history with deep snapshots
class UndoHistory {
  constructor(initial) {
    this.stack = [structuredClone(initial)];
    this.index = 0;
  }
  push(state) {
    this.stack.splice(this.index + 1); // discard redo states
    this.stack.push(structuredClone(state));
    this.index++;
  }
  undo() { return this.index > 0 ? structuredClone(this.stack[--this.index]) : null; }
  redo() { return this.index < this.stack.length - 1 ? structuredClone(this.stack[++this.index]) : null; }
}

8. Praktische Anwendungen: State-Management, Worker, Tests

Die drei häufigsten Anwendungsfälle für structuredClone() in produktiven JavaScript-Anwendungen sind State-Management, Worker-Kommunikation und Test-Fixtures. Im State-Management ist die tiefe Kopie des Zustands vor einer Mutation die Grundlage für Immutability: Statt den Zustand direkt zu modifizieren, klont man ihn, modifiziert den Klon und ersetzt den alten Zustand durch den neuen. Das erlaubt es, Snapshots zu vergleichen, Undo/Redo zu implementieren und Time-Travel-Debugging zu ermöglichen. Mit structuredClone() funktioniert das auch wenn der Zustand Maps, Sets oder Dates enthält.

Für Web Workers ist structuredClone() mit Transfer die Standard-Methode für effiziente Binärdaten-Übergabe. Statt einen ArrayBuffer zu kopieren, überträgt man die Ownership an den Worker – dieser kann die Daten sofort lesen, ohne dass die Haupt-Thread-Seite noch Zugriff hat. Bei der Rückkehr des Ergebnisses überträgt der Worker den Buffer zurück. Für Test-Fixtures ist structuredClone() die sauberste Methode, ein gemeinsames Fixture-Objekt zu isolieren: Jeder Test erhält eine unabhängige tiefe Kopie, ohne dass Test-Reihenfolge-Abhängigkeiten entstehen können.

9. Deep-Clone-Methoden im direkten Vergleich

Die Auswahl der richtigen Deep-Clone-Methode hängt von den Datentypen im zu klonenden Objekt ab. Die folgende Tabelle zeigt, welche Methode welche Anforderungen erfüllt.

Methode Date / RegExp Map / Set Zirkulär Funktionen
structuredClone() Korrekt Korrekt Korrekt Fehler
JSON.parse/stringify Date → String → {} TypeError Verloren
Lodash cloneDeep() Korrekt Korrekt Korrekt Referenz kopiert
Object spread {...obj} Flach Flach Endlosrekursion Referenz
Object.assign() Flach Flach Endlosrekursion Referenz

Die Empfehlung ist klar: Für Plain-Objects, Dates, Maps, Sets und ArrayBuffers ist structuredClone() die erste Wahl. Es ist nativ, korrekt, ohne externe Abhängigkeiten und in allen modernen Umgebungen verfügbar. Wenn Klasseninstanzen mit Prototypen geklont werden müssen, ist Lodash cloneDeep() die Alternative – wobei man bedenken sollte, dass Klasseninstanzen selten tief geklont werden müssen, weil deren Zustand meist in serialisierbaren Daten liegt. Für flache Kopien – wenn man nur die oberste Ebene eines Objekts klonen möchte ohne Verschachtelung – ist der Spread-Operator oder Object.assign() ausreichend und schneller.

Mironsoft

JavaScript-Code-Reviews, Modernisierung und robuste Frontend-Architekturen

JavaScript-Codebasis von fragilen Mustern bereinigen?

Wir identifizieren fragile JSON-Roundtrip-Muster, fehleranfällige Deep-Clone-Implementierungen und veraltete Abhängigkeiten in JavaScript-Codebasen und ersetzen sie durch native, zuverlässige APIs.

Code-Audit

JSON.parse/stringify-Muster finden, Lodash-Abhängigkeiten bewerten und structuredClone-Migrations-Plan erstellen

State-Management

Immutable-State-Pattern mit structuredClone() für Undo/Redo, Snapshots und Time-Travel-Debugging

Worker-Optimierung

Zero-Copy ArrayBuffer-Transfer für Web Worker – Binärverarbeitung ohne Speicher-Overhead

10. Zusammenfassung

structuredClone() ist die native Lösung für tiefes Kopieren in JavaScript, die seit 2022 in allen modernen Browsern, Node.js 17+ und Deno verfügbar ist. Es implementiert den Structured Clone Algorithm korrekt: Date, Map, Set, RegExp, ArrayBuffer, TypedArrays, zirkuläre Referenzen und gemeinsame Referenzen werden alle korrekt behandelt. Der JSON-Roundtrip-Trick verliert all diese Typen still und sollte nur noch für echte JSON-Serialisierung genutzt werden, nicht für Deep Cloning.

Die einzige Einschränkung von structuredClone(): Funktionen und Prototypen werden nicht geklont. Für Klasseninstanzen mit Methoden ist Lodash cloneDeep() die Alternative. Für alle anderen Szenarien – State-Management, Worker-Kommunikation, Test-Fixtures, Undo/Redo – ist structuredClone() die korrekteste, schnellste und abhängigkeitsfreieste Wahl. Die Transfer-Option für ArrayBuffer-Ownership macht zusätzlich eine Zero-Copy-Übergabe von Binärdaten möglich, die bisher nur über postMessage() erreichbar war.

structuredClone() — Das Wichtigste auf einen Blick

Korrekte Typen

Date, Map, Set, RegExp, ArrayBuffer, TypedArrays, zirkuläre Referenzen — alle korrekt geklont. JSON-Roundtrip verliert all diese Typen.

Einschränkungen

Keine Funktionen, keine Prototypen, keine DOM-Elemente. DataCloneError bei nicht-klon-baren Typen. Für Klasseninstanzen mit Methoden: Lodash cloneDeep().

Transfer-Option

structuredClone(obj, { transfer: [buffer] }) überträgt ArrayBuffer-Ownership ohne Kopieren. Original wird detached — Zero-Copy für Binärdaten.

Verfügbarkeit

Chrome 98+, Firefox 94+, Safari 15.4+, Node.js 17+, Deno. Global verfügbar ohne Import. Kein Polyfill für Zielumgebungen 2024+ nötig.

11. FAQ: structuredClone()

1structuredClone() vs. JSON-Roundtrip?
JSON verliert Date, Map, Set, undefined und wirft bei zirkulären Referenzen. structuredClone() behandelt alle korrekt. JSON nur für echte Serialisierung nutzen, nie für Deep Cloning.
2Klasseninstanzen mit Methoden klonen?
Nicht mit structuredClone() — Prototypen werden nicht geklont. clone instanceof MyClass ergibt false. Für Klasseninstanzen mit Methoden: Lodash cloneDeep().
3Was passiert mit Funktionen?
DataCloneError. Funktionen referenzieren Closures und Scopes — nicht serialisierbar. Objekte mit Funktions-Properties können nicht geklont werden.
4Was ist die Transfer-Option?
{ transfer: [arrayBuffer] } überträgt Ownership des ArrayBuffer ohne Kopieren. Original wird detached (byteLength = 0). Zero-Copy für effiziente Binärdaten-Übergabe.
5undefined-Werte korrekt?
Ja. undefined in Objekt-Properties bleibt erhalten. JSON löscht undefined-Properties aus Objekten. In Arrays konvertiert JSON undefined zu null — structuredClone() erhält auch das korrekt.
6Node.js-Verfügbarkeit?
Global seit Node.js 17.0.0 ohne Import. Ältere Node.js: v8.deserialize(v8.serialize(obj)) oder @ungap/structured-clone als Polyfill.
7Gemeinsame Referenzen?
Werden korrekt behandelt: Dasselbe Objekt wird nur einmal geklont, alle Referenzen im Klon zeigen auf dieselbe neue Kopie. Referenz-Topologie des Originals bleibt erhalten.
8Browser-Unterstützung?
Chrome 98+, Firefox 94+, Safari 15.4+, Edge 98+. Für ältere Browser: @ungap/structured-clone als npm-Polyfill. Global verfügbar ohne Import in allen unterstützten Umgebungen.
9Synchron oder asynchron?
Synchron — blockiert den Haupt-Thread. Für sehr große Objekte (viele MB) messbar. Für solche Fälle die Clone-Operation in einen Web Worker auslagern.
10Wann noch Lodash cloneDeep()?
Wenn Klasseninstanzen mit Prototypen und Methoden geklont werden müssen. Für alle anderen Szenarien ist structuredClone() vorzuziehen — nativ, schneller, keine externe Abhängigkeit.