Strukturiertes Klonen
2022 Aktualisierung: Die structuredClone
globale Funktion ist bereits in Firefox 94, Node 17 und Deno 1.14 verfügbar
Der HTML-Standard umfasst einen internen strukturierten Algorithmus zum Klonen/Serialisieren die tiefe Klone von Objekten erstellen kann. Es ist immer noch auf bestimmte eingebaute Typen beschränkt, aber zusätzlich zu den wenigen von JSON unterstützten Typen unterstützt es auch Dates, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, Typed Arrays und in Zukunft wahrscheinlich noch mehr. Außerdem werden Referenzen innerhalb der geklonten Daten beibehalten, so dass zyklische und rekursive Strukturen unterstützt werden, die bei JSON zu Fehlern führen würden.
Unterstützung in Node.js:
El structuredClone
globale Funktion wird von Node 17.0 bereitgestellt:
const clone = structuredClone(original);
Frühere Versionen: Die v8
Modul in Node.js (ab Node 11) stellt die strukturierte Serialisierungs-API direkt zur Verfügung Diese Funktion ist jedoch noch als "experimentell" gekennzeichnet und kann in zukünftigen Versionen geändert oder entfernt werden. Wenn Sie eine kompatible Version verwenden, ist das Klonen eines Objekts so einfach wie:
const v8 = require('v8');
const structuredClone = obj => {
return v8.deserialize(v8.serialize(obj));
};
Direkte Unterstützung in Browsern: Verfügbar in Firefox 94
El structuredClone
globale Funktion wird bald von allen wichtigen Browsern angeboten werden (dies wurde bereits in whatwg/html#793 auf GitHub ). Es sieht / wird so aussehen:
const clone = structuredClone(original);
Solange dies nicht der Fall ist, sind die strukturierten Klonimplementierungen der Browser nur indirekt sichtbar.
Asynchrone Abhilfe: Verwendbar.
Die kostengünstigere Möglichkeit, einen strukturierten Klon mit bestehenden APIs zu erstellen, besteht darin, die Daten über einen Port einer MessageChannels . Der andere Anschluss sendet ein message
Ereignis mit einem strukturierten Klon des angehängten .data
. Leider ist das Abhören dieser Ereignisse notwendigerweise asynchron, und die synchronen Alternativen sind weniger praktisch.
class StructuredCloner {
constructor() {
this.pendingClones_ = new Map();
this.nextKey_ = 0;
const channel = new MessageChannel();
this.inPort_ = channel.port1;
this.outPort_ = channel.port2;
this.outPort_.onmessage = ({data: {key, value}}) => {
const resolve = this.pendingClones_.get(key);
resolve(value);
this.pendingClones_.delete(key);
};
this.outPort_.start();
}
cloneAsync(value) {
return new Promise(resolve => {
const key = this.nextKey_++;
this.pendingClones_.set(key, resolve);
this.inPort_.postMessage({key, value});
});
}
}
const structuredCloneAsync = window.structuredCloneAsync =
StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner);
Beispiel Verwendung:
const main = async () => {
const original = { date: new Date(), number: Math.random() };
original.self = original;
const clone = await structuredCloneAsync(original);
// They're different objects:
console.assert(original !== clone);
console.assert(original.date !== clone.date);
// They're cyclical:
console.assert(original.self === original);
console.assert(clone.self === clone);
// They contain equivalent values:
console.assert(original.number === clone.number);
console.assert(Number(original.date) === Number(clone.date));
console.log("Assertions complete.");
};
main();
Synchrone Umgehungen: Schrecklich!
Es gibt keine guten Optionen für die synchrone Erstellung strukturierter Klone. Hier sind stattdessen ein paar unpraktische Hacks.
history.pushState()
y history.replaceState()
erstellen beide einen strukturierten Klon ihres ersten Arguments und weisen diesen Wert dem history.state
. Auf diese Weise können Sie einen strukturierten Klon eines beliebigen Objekts wie dieses erstellen:
const structuredClone = obj => {
const oldState = history.state;
history.replaceState(obj, null);
const clonedObj = history.state;
history.replaceState(oldState, null);
return clonedObj;
};
Beispiel Verwendung:
'use strict';
const main = () => {
const original = { date: new Date(), number: Math.random() };
original.self = original;
const clone = structuredClone(original);
// They're different objects:
console.assert(original !== clone);
console.assert(original.date !== clone.date);
// They're cyclical:
console.assert(original.self === original);
console.assert(clone.self === clone);
// They contain equivalent values:
console.assert(original.number === clone.number);
console.assert(Number(original.date) === Number(clone.date));
console.log("Assertions complete.");
};
const structuredClone = obj => {
const oldState = history.state;
history.replaceState(obj, null);
const clonedObj = history.state;
history.replaceState(oldState, null);
return clonedObj;
};
main();
Dies ist zwar synchron, kann aber extrem langsam sein. Es fällt der gesamte Overhead an, der mit der Bearbeitung des Browserverlaufs verbunden ist. Ein wiederholter Aufruf dieser Methode kann dazu führen, dass Chrome vorübergehend nicht mehr reagiert.
El Notification
Konstrukteur erstellt einen strukturierten Klon der zugehörigen Daten. Es wird auch versucht, dem Benutzer eine Browser-Benachrichtigung anzuzeigen, was jedoch fehlschlägt, es sei denn, Sie haben eine Benachrichtigungserlaubnis beantragt. Falls Sie die Erlaubnis für andere Zwecke haben, wird die von uns erstellte Benachrichtigung sofort geschlossen.
const structuredClone = obj => {
const n = new Notification('', {data: obj, silent: true});
n.onshow = n.close.bind(n);
return n.data;
};
Beispiel Verwendung:
'use strict';
const main = () => {
const original = { date: new Date(), number: Math.random() };
original.self = original;
const clone = structuredClone(original);
// They're different objects:
console.assert(original !== clone);
console.assert(original.date !== clone.date);
// They're cyclical:
console.assert(original.self === original);
console.assert(clone.self === clone);
// They contain equivalent values:
console.assert(original.number === clone.number);
console.assert(Number(original.date) === Number(clone.date));
console.log("Assertions complete.");
};
const structuredClone = obj => {
const n = new Notification('', {data: obj, silent: true});
n.close();
return n.data;
};
main();
566 Stimmen
Eval ist nicht böse. Schlechtes Benutzen von Eval schon. Wenn Sie Angst vor seinen Nebenwirkungen haben, verwenden Sie es falsch. Die Nebenwirkungen, die Sie fürchten, sind der Grund, warum Sie es verwenden. Hat eigentlich irgendjemand Ihre Frage beantwortet?
15 Stimmen
Das Klonen von Objekten ist eine knifflige Angelegenheit, insbesondere bei benutzerdefinierten Objekten beliebiger Sammlungen. Wahrscheinlich gibt es deshalb keine fertige Methode, dies zu tun.
12 Stimmen
eval()
ist im Allgemeinen eine schlechte Idee, weil viele Javascript-Engine-Optimierer abschalten müssen, wenn sie mit Variablen arbeiten, die übereval
. Nur miteval()
in Ihrem Code kann zu einer schlechteren Leistung führen.1 Stimmen
Hier ist ein Leistungsvergleich zwischen den gängigsten Arten von Klonobjekten: jsben.ch/#/t917Z
12 Stimmen
Beachten Sie, dass
JSON
Methode werden alle Javascript-Typen, die keine Entsprechung in JSON haben, verloren. Zum Beispiel:JSON.parse(JSON.stringify({a:null,b:NaN,c:Infinity,d:undefined,e:function(){},f:Number,g:false}))
erzeugt{a: null, b: null, c: null, g: false}
0 Stimmen
Die React-Gemeinschaft hat Folgendes eingeführt Unveränderlichkeits-Hilfsmittel