Was ist der effizienteste Weg, um ein JavaScript-Objekt zu klonen? Ich habe gesehen, obj = eval(uneval(o));
dass es verwendet wird, aber das ist nicht Standard und wird nur von Firefox unterstützt .
Ich habe Dinge getan, obj = JSON.parse(JSON.stringify(o));
aber die Effizienz in Frage gestellt.
Ich habe auch rekursive Kopierfunktionen mit verschiedenen Fehlern gesehen.
Ich bin überrascht, dass es keine kanonische Lösung gibt.
javascript
object
clone
jschrab
quelle
quelle
eval()
Dieseval
ist im Allgemeinen eine schlechte Idee, da viele Optimierer der Javascript-Engine deaktiviert werden müssen, wenn sie mit Variablen arbeiten, die über festgelegt werden . Nureval()
in Ihrem Code zu haben, kann zu einer schlechteren Leistung führen.JSON
Methode alle Javascript-Typen verliert, die in JSON keine Entsprechung haben. Zum Beispiel:JSON.parse(JSON.stringify({a:null,b:NaN,c:Infinity,d:undefined,e:function(){},f:Number,g:false}))
generiert{a: null, b: null, c: null, g: false}
Antworten:
Native Deep Cloning
Es heißt "strukturiertes Klonen", funktioniert experimentell in Knoten 11 und höher und wird hoffentlich in Browsern landen. Siehe diese Antwort für weitere Details.
Schnelles Klonen mit Datenverlust - JSON.parse / stringify
Wenn Sie nicht verwenden
Date
s, Funktionenundefined
,Infinity
, regexps, Karten, Sets, Blobs, Dateilisten, ImageDatas, Sparse - Matrizen, typisiert Arrays oder andere komplexe Typen in Ihrem Objekt, ein sehr einfacher Einzeiler zu tiefer Klon eines Objekt ist:JSON.parse(JSON.stringify(object))
Benchmarks finden Sie in Corbans Antwort .
Zuverlässiges Klonen mithilfe einer Bibliothek
Da das Klonen von Objekten nicht trivial ist (komplexe Typen, Zirkelverweise, Funktionen usw.), bieten die meisten Hauptbibliotheken Funktionen zum Klonen von Objekten. Erfinden Sie das Rad nicht neu. Wenn Sie bereits eine Bibliothek verwenden, überprüfen Sie, ob diese über eine Funktion zum Klonen von Objekten verfügt. Zum Beispiel,
cloneDeep
; kann separat über das lodash.clonedeep- Modul importiert werden und ist wahrscheinlich die beste Wahl, wenn Sie noch keine Bibliothek verwenden, die eine Deep Cloning-Funktion bietetangular.copy
jQuery.extend(true, { }, oldObject)
;.clone()
Klont nur DOM-ElementeES6
Beachten Sie der Vollständigkeit halber, dass ES6 zwei flache Kopiermechanismen bietet:
Object.assign()
und die Spread-Syntax . Hiermit werden Werte aller aufzählbaren eigenen Eigenschaften von einem Objekt in ein anderes kopiert. Zum Beispiel:quelle
.clone()
, was nicht der richtige Code ist Verwendung in diesem Zusammenhang). Leider hat diese Frage so viele Überarbeitungen durchlaufen, dass die ursprüngliche Diskussion nicht mehr sichtbar ist! Befolgen Sie einfach die Anweisungen von Corban und schreiben Sie eine Schleife oder kopieren Sie die Eigenschaften direkt in ein neues Objekt, wenn Sie Wert auf Geschwindigkeit legen. Oder testen Sie es selbst!Überprüfen Sie diesen Benchmark: http://jsben.ch/#/bWfk9
In meinen vorherigen Tests, bei denen Geschwindigkeit ein Hauptanliegen war, fand ich
Dies ist der langsamste Weg, um ein Objekt tief zu klonen (es ist langsamer als jQuery.extend, wobei das
deep
Flag um 10-20% auf true gesetzt ist).jQuery.extend ist ziemlich schnell, wenn das
deep
Flag auffalse
(flacher Klon) gesetzt ist. Dies ist eine gute Option, da sie eine zusätzliche Logik für die Typüberprüfung enthält und nicht über undefinierte Eigenschaften usw. kopiert. Dies verlangsamt Sie jedoch auch ein wenig.Wenn Sie die Struktur der Objekte kennen, die Sie klonen
for (var i in obj)
möchten, oder tief verschachtelte Arrays vermeiden können, können Sie eine einfache Schleife schreiben , um Ihr Objekt zu klonen, während Sie hasOwnProperty überprüfen. Diese ist viel schneller als jQuery.Wenn Sie versuchen, eine bekannte Objektstruktur in einer Hot-Loop zu klonen, können Sie VIEL MEHR LEISTUNG erzielen, indem Sie einfach die Klonprozedur einbinden und das Objekt manuell erstellen.
JavaScript-Trace-Engines sind nicht in der
for..in
Lage, Schleifen zu optimieren , und die Überprüfung von hasOwnProperty verlangsamt Sie ebenfalls. Manuelles Klonen, wenn Geschwindigkeit ein absolutes Muss ist.Vorsicht bei der Verwendung der
JSON.parse(JSON.stringify(obj))
Methode fürDate
Objekte -JSON.stringify(new Date())
Gibt eine Zeichenfolgendarstellung des Datums im ISO-Format zurück, dieJSON.parse()
nicht zurück in einDate
Objekt konvertiert wird. Siehe diese Antwort für weitere Details .Beachten Sie außerdem, dass zumindest in Chrome 65 das native Klonen nicht der richtige Weg ist. Laut JSPerf ist das native Klonen durch Erstellen einer neuen Funktion fast 800-mal langsamer als die Verwendung von JSON.stringify, das auf der ganzen Linie unglaublich schnell ist.
Update für ES6
Wenn Sie Javascript ES6 verwenden, versuchen Sie diese native Methode zum Klonen oder für flache Kopien.
quelle
keys
aus Ihrenobject
, diefunctions
als Werte haben, da dieJSON
keine Funktionen unterstützt.JSON.parse(JSON.stringify(obj))
auch, dass bei Verwendung von On-Date-Objekten das Datum in der Zeichenfolgendarstellung im ISO8601- Format auch wieder in UTC konvertiert wird.Angenommen, Sie haben nur Variablen und keine Funktionen in Ihrem Objekt, können Sie einfach Folgendes verwenden:
quelle
JSON
jedoch in nativem Code implementiert ist (in den meisten Browsern), ist dies erheblich schneller als die Verwendung einer anderen Javascript-basierten Deep-Copying-Lösung und manchmal auch schneller als eine Javascript-basierte Flachkopiertechnik (siehe: jsperf.com/cloning) -an-Objekt / 79 ).JSON.stringify({key: undefined}) //=> "{}"
Date
Objekte, die im Objekt gespeichert sind, und konvertiert sie in eine Zeichenfolgenform.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: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: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.Beispiel Verwendung:
Synchrone Problemumgehungen: Schrecklich! 🤢
Es gibt keine guten Optionen zum synchronen Erstellen strukturierter Klone. Hier sind stattdessen ein paar unpraktische Hacks.
history.pushState()
undhistory.replaceState()
beide erstellen einen strukturierten Klon ihres ersten Arguments und weisen diesen Wert zuhistory.state
. Sie können dies verwenden, um einen strukturierten Klon eines Objekts wie dieses zu erstellen:Beispiel Verwendung:
Code-Snippet anzeigen
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.Beispiel Verwendung:
Code-Snippet anzeigen
quelle
history.pushState()
und setzenhistory.replaceState()
beide synchronhistory.state
auf einen strukturierten Klon ihres ersten Arguments. Ein bisschen komisch, aber es funktioniert. Ich aktualisiere jetzt meine Antwort.Wenn es keine eingebaute gäbe, könnten Sie versuchen:
quelle
Der effiziente Weg, ein Objekt in einer Codezeile zu klonen (nicht tief zu klonen)
Eine
Object.assign
Methode ist Teil des ECMAScript 2015 (ES6) -Standards und macht genau das, was Sie brauchen.Weiterlesen...
Die Polyfüllung zur Unterstützung älterer Browser:
quelle
Code:
Prüfung:
quelle
var obj = {}
undobj.a = obj
from.constructor
istDate
zum Beispiel. Wie würde der dritteif
Test erreicht werden, wenn der zweiteif
Test erfolgreich wäre und die Funktion (seitDate != Object && Date != Array
) zurückkehren würde?Das benutze ich:
quelle
cloneObject({ name: null })
=>{"name":{}}
typeof null > "object"
aberObject.keys(null) > TypeError: Requested keys of a value that is not an object.
ändern Sie die Bedingung inif(typeof(obj[i])=="object" && obj[i]!=null)
Tiefe Kopie nach Leistung: Vom Besten zum Schlechtesten
Kopieren Sie ein Array von Zeichenfolgen oder Zahlen tief (eine Ebene - keine Referenzzeiger):
Wenn ein Array Zahlen und Zeichenfolgen enthält - Funktionen wie .slice (), .concat (), .splice (), der Zuweisungsoperator "=" und die Klonfunktion von Underscore.js; erstellt eine tiefe Kopie der Elemente des Arrays.
Wo Neuzuweisung die schnellste Leistung hat:
Und .slice () hat eine bessere Leistung als .concat (), http://jsperf.com/duplicate-array-slice-vs-concat/3
Kopieren Sie ein Array von Objekten tief (zwei oder mehr Ebenen - Referenzzeiger):
Schreiben Sie eine benutzerdefinierte Funktion (hat eine schnellere Leistung als $ .extend () oder JSON.parse):
Verwenden Sie Dienstprogrammfunktionen von Drittanbietern:
Wo jQuerys $ .extend eine bessere Leistung hat:
quelle
out[key] = (typeof v === "object" && v !== null) ? copy(v) : v;
quelle
Tiefes Kopieren von Objekten in JavaScript (ich denke das Beste und das Einfachste)
1. Verwenden von JSON.parse (JSON.stringify (Objekt));
2. Mit der erstellten Methode
3. Verwenden des _.cloneDeep- Link- Lodash von Lo-Dash
4. Verwenden der Object.assign () -Methode
ABER FALSCH WANN
5.Verwenden von Underscore.js _.clone link Underscore.js
ABER FALSCH WANN
JSBEN.CH Performance Benchmarking Playground 1 ~ 3 http://jsben.ch/KVQLd
quelle
Object.assign()
führt keine tiefe Kopie durchlet data = {title:["one", "two"]}; let tmp = cloneObject(data); tmp.title.pop();
es wirft:TypeError: tmp.title.pop is not a function
(natürlich pop () funktioniert gut, wenn ich nurdo let tmp = data
; aber dann kann ich tmp nicht ändern, ohne Daten zu beeinflussen)Es gibt eine Bibliothek ("Klon" genannt) , die das ganz gut macht. Es bietet das vollständigste rekursive Klonen / Kopieren von beliebigen Objekten, die mir bekannt sind. Es werden auch Zirkelverweise unterstützt, die in den anderen Antworten noch nicht behandelt werden.
Sie finden es auch auf npm . Es kann sowohl für den Browser als auch für Node.js verwendet werden.
Hier ist ein Beispiel für die Verwendung:
Installieren Sie es mit
oder verpacken Sie es mit Ender .
Sie können den Quellcode auch manuell herunterladen.
Dann können Sie es in Ihrem Quellcode verwenden.
(Haftungsausschluss: Ich bin der Autor der Bibliothek.)
quelle
JSON.parse(JSON.stringify(obj))
?Cloning
Ein Objekt war in JS immer ein Problem, aber es ging um ES6. Ich liste unten verschiedene Möglichkeiten zum Kopieren eines Objekts in JavaScript auf. Stellen Sie sich vor, Sie haben das Objekt unten und möchten eine ausführliche Kopie davon haben:Es gibt nur wenige Möglichkeiten, dieses Objekt zu kopieren, ohne den Ursprung zu ändern:
1) ES5 +, Verwenden einer einfachen Funktion, um die Kopie für Sie zu erstellen:
2) ES5 + mit JSON.parse und JSON.stringify.
3) AngularJs:
4) jQuery:
5) Unterstriche & Loadash:
Hoffe diese Hilfe ...
quelle
Object.assign
ist nicht tief kopieren. Beispiel :var x = { a: { b: "c" } }; var y = Object.assign({}, x); x.a.b = "d"
. Wenn dies eine tiefe Kopiey.a.b
wäre , wäre es immer nochc
, aber es ist jetztd
.Ich weiß, dass dies ein alter Beitrag ist, aber ich dachte, dass dies für die nächste Person, die mit stolpert, hilfreich sein könnte.
Solange Sie nichts einem Objekt zuweisen, behält es keine Referenz im Speicher. Um ein Objekt zu erstellen, das Sie mit anderen Objekten teilen möchten, müssen Sie eine Fabrik wie folgt erstellen:
quelle
const defaultFoo = { a: { b: 123 } };
gehen könnenconst defaultFoo = () => ({ a: { b: 123 } };
und Ihr Problem gelöst ist. Es ist jedoch wirklich keine Antwort auf die Frage. Als Kommentar zu der Frage hätte es vielleicht mehr Sinn gemacht, als als vollständige Antwort.Wenn Sie es verwenden, verfügt die Bibliothek Underscore.js über eine Klonmethode .
quelle
.clone(...)
Methode einer Dienstprogrammbibliothek sind . Jede größere Bibliothek wird sie haben, und die wiederholten kurzen, nicht detaillierten Antworten sind für die meisten Besucher, die diese bestimmte Bibliothek nicht verwenden, nicht nützlich.Hier ist eine Version der obigen Antwort von ConroyP, die auch dann funktioniert, wenn der Konstruktor Parameter benötigt:
Diese Funktion ist auch in meiner simpleoo- Bibliothek verfügbar .
Bearbeiten:
Hier ist eine robustere Version (dank Justin McCandless unterstützt dies jetzt auch zyklische Referenzen):
quelle
Im Folgenden werden zwei Instanzen desselben Objekts erstellt. Ich habe es gefunden und benutze es derzeit. Es ist einfach und leicht zu bedienen.
quelle
Crockford schlägt vor (und ich bevorzuge), diese Funktion zu verwenden:
Es ist knapp, funktioniert wie erwartet und Sie brauchen keine Bibliothek.
BEARBEITEN:
Dies ist eine Polyfüllung für
Object.create
, sodass Sie diese auch verwenden können.HINWEIS: Wenn Sie einige davon verwenden, haben Sie möglicherweise Probleme mit einigen Iterationen, die Sie verwenden
hasOwnProperty
. Denncreate
neues leeres Objekt erstellen , die erbenoldObject
. Es ist jedoch immer noch nützlich und praktisch zum Klonen von Objekten.Zum Beispiel wenn
oldObject.a = 5;
aber:
quelle
var extendObj = function(childObj, parentObj) { var tmpObj = function () {} tmpObj.prototype = parentObj.prototype; childObj.prototype = new tmpObj(); childObj.prototype.constructor = childObj; };
... davidshariff.com/blog/javascript-inheritance-patternsLodash hat eine schöne _.cloneDeep (value) -Methode:
quelle
.clone(...)
Methode einer Dienstprogrammbibliothek sind . Jede größere Bibliothek wird sie haben, und die wiederholten kurzen, nicht detaillierten Antworten sind für die meisten Besucher, die diese bestimmte Bibliothek nicht verwenden, nicht nützlich._.merge({}, objA)
. Wenn nur lodash Objekte überhaupt nicht mutierenclone
würde, wäre die Funktion nicht notwendig.quelle
Einzeiler mit flacher Kopie ( ECMAScript 5. Ausgabe ):
Und flacher Einzeiler ( ECMAScript 6. Ausgabe , 2015):
quelle
Object.keys
nicht aufzählbare und geerbte Eigenschaften übersprungen. Außerdem verliert es durch direkte Zuweisung Eigenschaftsbeschreibungen.Nur weil ich AngularJS nicht erwähnt habe und dachte, dass die Leute es wissen wollen ...
angular.copy
bietet auch eine Methode zum tiefen Kopieren von Objekten und Arrays.quelle
angular.extend({},obj);
jQuery.extend
undangular.extend
beide flache Kopien sind.angular.copy
ist eine tiefe Kopie.Es scheint noch keinen idealen Deep-Clone-Operator für Array-ähnliche Objekte zu geben. Wie der folgende Code zeigt, wandelt der jQuery-Cloner von John Resig Arrays mit nicht numerischen Eigenschaften in Objekte um, die keine Arrays sind, und der JSON-Cloner von RegDwight löscht die nicht numerischen Eigenschaften. Die folgenden Tests veranschaulichen diese Punkte in mehreren Browsern:
quelle
Ich habe zwei gute Antworten, je nachdem, ob Ihr Ziel darin besteht, ein "einfaches altes JavaScript-Objekt" zu klonen oder nicht.
Nehmen wir außerdem an, dass Sie beabsichtigen, einen vollständigen Klon ohne Prototypverweise auf das Quellobjekt zu erstellen. Wenn Sie nicht an einem vollständigen Klon interessiert sind, können Sie viele der Object.clone () -Routinen verwenden, die in einigen anderen Antworten (Crockfords Muster) enthalten sind.
Für einfache alte JavaScript-Objekte ist eine bewährte Methode zum Klonen eines Objekts in modernen Laufzeiten ganz einfach:
Beachten Sie, dass das Quellobjekt ein reines JSON-Objekt sein muss. Dies bedeutet, dass alle verschachtelten Eigenschaften Skalare sein müssen (wie Boolesche Werte, Zeichenfolgen, Arrays, Objekte usw.). Funktionen oder spezielle Objekte wie RegExp oder Date werden nicht geklont.
Ist es effizient? Oh Ja. Wir haben alle Arten von Klonmethoden ausprobiert und dies funktioniert am besten. Ich bin sicher, ein Ninja könnte eine schnellere Methode zaubern. Aber ich vermute, wir sprechen von geringfügigen Gewinnen.
Dieser Ansatz ist einfach und leicht zu implementieren. Wickeln Sie es in eine Komfortfunktion ein, und wenn Sie wirklich etwas Gewinn herausholen müssen, versuchen Sie es zu einem späteren Zeitpunkt.
Für nicht einfache JavaScript-Objekte gibt es keine wirklich einfache Antwort. In der Tat kann es aufgrund der Dynamik der JavaScript-Funktionen und des inneren Objektzustands keine geben. Um eine JSON-Struktur mit darin enthaltenen Funktionen tief zu klonen, müssen Sie diese Funktionen und ihren inneren Kontext neu erstellen. Und JavaScript hat einfach keine standardisierte Methode, um dies zu tun.
Der richtige Weg, dies erneut zu tun, ist eine bequeme Methode, die Sie in Ihrem Code deklarieren und wiederverwenden. Die Convenience-Methode kann mit einem gewissen Verständnis Ihrer eigenen Objekte ausgestattet werden, sodass Sie sicherstellen können, dass das Diagramm innerhalb des neuen Objekts ordnungsgemäß neu erstellt wird.
Wir haben unsere eigenen geschrieben, aber der beste allgemeine Ansatz, den ich gesehen habe, wird hier behandelt:
http://davidwalsh.name/javascript-clone
Das ist die richtige Idee. Der Autor (David Walsh) hat das Klonen generalisierter Funktionen auskommentiert. Dies können Sie je nach Anwendungsfall tun.
Die Hauptidee ist, dass Sie die Instanziierung Ihrer Funktionen (oder sozusagen prototypischer Klassen) pro Typ speziell behandeln müssen. Hier hat er einige Beispiele für RegExp und Date angegeben.
Dieser Code ist nicht nur kurz, sondern auch sehr gut lesbar. Es ist ziemlich einfach zu erweitern.
Ist das effizient? Oh Ja. Da das Ziel darin besteht, einen echten Deep-Copy-Klon zu erstellen, müssen Sie die Mitglieder des Quellobjektdiagramms durchlaufen. Mit diesem Ansatz können Sie genau festlegen, welche untergeordneten Mitglieder behandelt werden sollen und wie benutzerdefinierte Typen manuell behandelt werden sollen.
Hier bitteschön. Zwei Ansätze. Beide sind meiner Ansicht nach effizient.
quelle
Dies ist im Allgemeinen nicht die effizienteste Lösung, aber sie macht das, was ich brauche. Einfache Testfälle unten ...
Zyklischer Array-Test ...
Funktionstest...
quelle
AngularJS
Wenn Sie Angular verwenden, können Sie dies auch tun
quelle
Ich bin mit der Antwort mit den größten Stimmen hier nicht einverstanden . Ein rekursiver Deep Clone ist viel schneller als der erwähnte Ansatz von JSON.parse (JSON.stringify (obj)) .
Und hier ist die Funktion zum schnellen Nachschlagen:
quelle
if(o instanceof Date) return new Date(o.valueOf());
nachdem Sie nach null gesucht haben `quelle
Nur wenn Sie ECMAScript 6 oder Transpiler verwenden können .
Eigenschaften:
Code:
quelle
Hier ist eine umfassende clone () -Methode, mit der jedes JavaScript-Objekt geklont werden kann. Es behandelt fast alle Fälle:
quelle