Strukturiertes Klonen
Der HTML-Standard enthält einen internen strukturierten Klon- / Serialisierungsalgorithmus , mit dem tiefe Klone von Objekten erstellt werden können. Es ist immer noch auf bestimmte integrierte Typen beschränkt, unterstützt aber zusätzlich zu den wenigen von JSON unterstützten Typen auch Dates, RegExps, Maps, Sets, Blobs, Dateilisten, ImageDatas, spärliche Arrays, typisierte Arrays und wahrscheinlich auch in Zukunft . Außerdem werden Verweise in den geklonten Daten beibehalten, sodass zyklische und rekursive Strukturen unterstützt werden können, die Fehler für JSON verursachen würden.
Unterstützung in Node.js: Experimentell 🙂
Das v8
Modul in Node.js stellt derzeit (ab Knoten 11) die strukturierte Serialisierungs-API direkt zur Verfügung . Diese Funktionalität ist jedoch weiterhin 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));
};
Direkter Support in Browsern: Vielleicht irgendwann? 😐
Browser bieten derzeit keine direkte Schnittstelle für den strukturierten Klonalgorithmus , aber eine globale structuredClone()
Funktion wurde in whatwg / html # 793 auf GitHub erläutert . Wie derzeit vorgeschlagen, wäre die Verwendung für die meisten Zwecke so einfach wie:
const clone = structuredClone(original);
Sofern dies nicht ausgeliefert wird, werden die strukturierten Klonimplementierungen von Browsern nur indirekt verfügbar gemacht.
Asynchrone Problemumgehung: Verwendbar. 😕
Der kostengünstigere Weg, einen strukturierten Klon mit vorhandenen APIs zu erstellen, besteht darin, die Daten über einen Port eines MessageChannels zu senden . Der andere Port gibt ein message
Ereignis mit einem strukturierten Klon des angehängten aus .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 Problemumgehungen: Schrecklich! 🤢
Es gibt keine guten Optionen zum synchronen Erstellen strukturierter Klone. Hier sind stattdessen ein paar unpraktische Hacks.
history.pushState()
und history.replaceState()
beide erstellen einen strukturierten Klon ihres ersten Arguments und weisen diesen Wert zu history.state
. Sie können dies verwenden, um einen strukturierten Klon eines Objekts wie dieses zu 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();
Obwohl synchron, kann dies extrem langsam sein. Es entsteht der gesamte Aufwand für die Bearbeitung des Browserverlaufs. Das wiederholte Aufrufen dieser Methode kann dazu führen, dass Chrome vorübergehend nicht mehr reagiert.
Der Notification
Konstruktor erstellt einen strukturierten Klon der zugehörigen Daten. Es wird auch versucht, dem Benutzer eine Browserbenachrichtigung anzuzeigen. Dies schlägt jedoch unbemerkt fehl, es sei denn, Sie haben eine Benachrichtigungsberechtigung angefordert. Falls Sie die Erlaubnis für andere Zwecke haben, schließen wir die von uns erstellte Benachrichtigung sofort.
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();