Was ist der effizienteste Weg, um tief Klon ein Objekt in JavaScript?
-
02-07-2019 - |
Frage
Was ist der effizienteste Weg, um ein JavaScript-Objekt zu klonen? Ich habe obj = eval(uneval(o));
gesehen verwendet wird, aber das ist nicht-Standard und nur von Firefox unterstützt.
ich habe Dinge getan, wie obj = JSON.parse(JSON.stringify(o));
aber die Effizienz in Frage stellen.
Ich habe auch rekursive Kopierfunktionen mit verschiedenen Fehlern gesehen.
Ich bin überrascht, keine kanonische Lösung vorhanden ist.
Lösung
2019-Juni Hinweis: Dies war ursprünglich eine Antwort auf eine andere Antwort, keine richtige Antwort auf diese Frage. Keine Ahnung, warum es wurde als die richtige Antwort ausgewählt. Da aber die upvotes lawinenartig und es ist bei weitem der # 1 Antwort auf diese Frage, wird es die Lösungen als Wiki Antwort zusammenfassen.
Native tief Klonen
Es ist „strukturiertes Klonen“ genannt, arbeitet experimentell in Knoten 11 und später, und hoffentlich in Browsern landen wird. Siehe diese beantworten für weitere Details.
Fast Klonen mit Datenverlust - JSON.parse / stringify
Wenn Sie nicht verwenden Date
s, Funktionen, undefined
, 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 einer Objekt:
JSON.parse(JSON.stringify(object))
const a = {
string: 'string',
number: 123,
bool: false,
nul: null,
date: new Date(), // stringified
undef: undefined, // lost
inf: Infinity, // forced to 'null'
re: /.*/, // lost
}
console.log(a);
console.log(typeof a.date); // Date object
const clone = JSON.parse(JSON.stringify(a));
console.log(clone);
console.log(typeof clone.date); // result of .toISOString()
Siehe cloneDeep
; kann separat lodash.clonedeep -Modul über die und wahrscheinlich die beste Wahl, wenn Sie importiert werden‘ re nicht bereits eine Bibliothek, die eine tiefe Klonfunktion liefert
angular.copy
jQuery.extend(true, { }, oldObject)
; .clone()
nur Klone DOM-Elemente ES6
Für die Vollständigkeit, beachten Sie, dass ES6 zwei flache Kopie Mechanismen bietet: Object.assign()
und der Verbreitung Operator .
Andere Tipps
Zur Kasse dieser Benchmark: http://jsben.ch/#/bWfk9
In meinem vorherigen Test, bei denen Geschwindigkeit ein Hauptanliegen war, fand ich
JSON.parse(JSON.stringify(obj))
der langsamste Weg zu tiefer Klon sein ein Objekt (es ist langsamer als jQuery.extend mit deep
Flag gesetzt wahr um 10-20%).
jQuery.extend ziemlich schnell ist, wenn der deep
-Flag gesetzt ist auf false
(flache Klon). Es ist eine gute Wahl, weil es einige zusätzliche Logik für die Art Validierung umfasst und nicht über undefinierte Eigenschaften kopiert, usw., aber das wird man auch langsam nach unten ein wenig.
Wenn Sie wissen, die Struktur der Objekte, die Sie zu klonen versuchen oder tief verschachtelten Arrays vermeiden Sie eine einfache for (var i in obj)
Schleife schreiben können Ihr Objekt zu klonen, während hasOwnProperty prüft und es wird viel viel schneller als jQuery.
Schließlich, wenn Sie eine bekannte Objektstruktur in einer heißen Schleife zu klonen versuchen Sie viel viel mehr PERFORMANCE einfach in-Auskleidung der Klon Verfahren erhalten können und das Objekt manuell zu konstruieren.
JavaScript Spur Motoren saugen an for..in
Schleifen Optimierung und Kontrolle hasOwnProperty werden Sie auch verlangsamen. Manuelles Klon, wenn die Geschwindigkeit ist ein absolutes Muss.
var clonedObject = {
knownProp: obj.knownProp,
..
}
Vorsicht vor der JSON.parse(JSON.stringify(obj))
Methode auf Date
Objekten mit - JSON.stringify(new Date())
gibt eine Zeichenfolge Darstellung des Datums im ISO-Format, die JSON.parse()
nicht konvertieren zurück zu einem Date
Objekt. Sehen Sie diese Antwort für weitere Details .
Zusätzlich Bitte beachten Sie, dass in Chrome 65 zumindest native Klonen ist nicht der Weg zu gehen. Nach dieser JSPerf , nativen Klonen Durchführung durch eine neue Funktion zu schaffen ist fast 800x langsamer als JSON.stringify verwendet, die unglaublich schnell den ganzen Weg auf der ganzen Linie ist.
Wenn Sie mit Javascript ES6 versuchen, diese native Methode für das Klonen oder flache Kopie.
Object.assign({}, obj);
Unter der Annahme, dass Sie nur Variablen und keine Funktionen in Ihrem Objekt, können Sie einfach verwenden:
var newObject = JSON.parse(JSON.stringify(oldObject));
Strukturierte Cloning
Der HTML-Standard enthält eine interne strukturierte Klonen / Serialisierung Algorithmus das kann tief Klonen von Objekten erstellen. Es ist immer noch beschränkt auf bestimmte Typen-in gebaut, aber zusätzlich zu den wenigen Arten unterstützt von JSON unterstützt es auch Termine, regexps, Karten, Sets, Blobs, Dateilisten, ImageDatas, Sparse-Matrizen, typisiert Arrays, und wahrscheinlich in der Zukunft . Es bewahrt auch Verweise innerhalb der geklonten Daten, so dass sie zyklisch und rekursive Strukturen zu unterstützen, die Fehler für JSON verursachen würde.
Support in Node.js: Experimental
Wenn es keine builtin war, könnten Sie versuchen:
function clone(obj) {
if (obj === null || typeof (obj) !== 'object' || 'isActiveClone' in obj)
return obj;
if (obj instanceof Date)
var temp = new obj.constructor(); //or new Date(obj);
else
var temp = obj.constructor();
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
obj['isActiveClone'] = null;
temp[key] = clone(obj[key]);
delete obj['isActiveClone'];
}
}
return temp;
}
Die effiziente Art und Weise zu klonen (nicht tief Klon) ein Objekt in einer Zeile Code
Ein Object.assign
Verfahren ist ein Teil von die ECMAScript 2015 (ES6) Standard und macht genau das, was Sie brauchen.
var clone = Object.assign({}, obj);
Die Object.assign () Methode wird verwendet, um die Werte aller enumerable eigenen Eigenschaften von einem oder mehreren Quellobjekte zu einem Zielobjekt zu kopieren.
Die polyfill ältere Browser unterstützen:
if (!Object.assign) {
Object.defineProperty(Object, 'assign', {
enumerable: false,
configurable: true,
writable: true,
value: function(target) {
'use strict';
if (target === undefined || target === null) {
throw new TypeError('Cannot convert first argument to object');
}
var to = Object(target);
for (var i = 1; i < arguments.length; i++) {
var nextSource = arguments[i];
if (nextSource === undefined || nextSource === null) {
continue;
}
nextSource = Object(nextSource);
var keysArray = Object.keys(nextSource);
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
var nextKey = keysArray[nextIndex];
var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
if (desc !== undefined && desc.enumerable) {
to[nextKey] = nextSource[nextKey];
}
}
}
return to;
}
});
}
Code:
// extends 'from' object with members from 'to'. If 'to' is null, a deep clone of 'from' is returned
function extend(from, to)
{
if (from == null || typeof from != "object") return from;
if (from.constructor != Object && from.constructor != Array) return from;
if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function ||
from.constructor == String || from.constructor == Number || from.constructor == Boolean)
return new from.constructor(from);
to = to || new from.constructor();
for (var name in from)
{
to[name] = typeof to[name] == "undefined" ? extend(from[name], null) : to[name];
}
return to;
}
Test:
var obj =
{
date: new Date(),
func: function(q) { return 1 + q; },
num: 123,
text: "asdasd",
array: [1, "asd"],
regex: new RegExp(/aaa/i),
subobj:
{
num: 234,
text: "asdsaD"
}
}
var clone = extend(obj);
Dies ist, was ich verwende:
function cloneObject(obj) {
var clone = {};
for(var i in obj) {
if(typeof(obj[i])=="object" && obj[i] != null)
clone[i] = cloneObject(obj[i]);
else
clone[i] = obj[i];
}
return clone;
}
Deep Kopie durch Leistung: Auf Rang vom besten zum schlechtesten
- Neuzuordnung "=" (String-Arrays, die Anzahl Arrays - nur)
- Scheibe (String-Arrays, Zahlen-Arrays - nur)
- Verkettungs (String-Arrays, die Anzahl Arrays - nur)
- Benutzerdefinierte Funktion: for-Schleife oder rekursive Kopie
- jQuery $ .extend
- JSON.parse (String-Arrays, die Anzahl Arrays, Objekt-Arrays - nur)
- Underscore.js 's _.clone (String-Arrays, Zahlen-Arrays - nur )
- Lo-Dash _.cloneDeep
Deep Kopie ein Array von Strings oder Zahlen (eine Ebene - keine Referenzzeiger):
Wenn ein Array enthält Zahlen und Strings - Funktionen wie .slice (), .concat (), .splice (), den Zuweisungsoperator "=" und Underscore.js des Klons Funktion; wird eine tiefe Kopie der Elemente des Arrays machen.
Wo Neuzuordnung hat die schnellste Leistung:
var arr1 = ['a', 'b', 'c'];
var arr2 = arr1;
arr1 = ['a', 'b', 'c'];
Und .slice () hat eine bessere Leistung als .concat (), http://jsperf.com/duplicate-array-slice-vs-concat/3
var arr1 = ['a', 'b', 'c']; // Becomes arr1 = ['a', 'b', 'c']
var arr2a = arr1.slice(0); // Becomes arr2a = ['a', 'b', 'c'] - deep copy
var arr2b = arr1.concat(); // Becomes arr2b = ['a', 'b', 'c'] - deep copy
Deep Kopie ein Array von Objekten (zwei oder mehr Ebenen - Referenzzeiger):
var arr1 = [{object:'a'}, {object:'b'}];
Schreiben Sie eine benutzerdefinierte Funktion (hat schnellere Leistung als $ .extend () oder JSON.parse):
function copy(o) {
var out, v, key;
out = Array.isArray(o) ? [] : {};
for (key in o) {
v = o[key];
out[key] = (typeof v === "object" && v !== null) ? copy(v) : v;
}
return out;
}
copy(arr1);
Verwendung von Drittanbieter-Dienstprogramm Funktionen:
$.extend(true, [], arr1); // Jquery Extend
JSON.parse(arr1);
_.cloneDeep(arr1); // Lo-dash
Wo jQuery $ .extend hat eine bessere Leistung:
var clone = function() {
var newObj = (this instanceof Array) ? [] : {};
for (var i in this) {
if (this[i] && typeof this[i] == "object") {
newObj[i] = this[i].clone();
}
else
{
newObj[i] = this[i];
}
}
return newObj;
};
Object.defineProperty( Object.prototype, "clone", {value: clone, enumerable: false});
Es gibt eine Bibliothek (genannt „Klon“) , dass dies ganz gut tut. Es bietet die vollständigste rekursive Klonen / Kopieren von beliebigen Objekten, die ich kenne. Es unterstützt auch zirkuläre Referenzen, die nicht von den anderen Antworten bedeckt ist, vor.
Sie können finden rel="noreferrer"> auch. Es kann für den Browser sowie Node.js verwendet werden.
Hier ist ein Beispiel, wie man es verwenden:
Installieren Sie es mit
npm install clone
oder verpacken sie mit Ender .
ender build clone [...]
Sie können auch den Quellcode manuell herunterladen.
Dann können Sie es in Ihrem Quellcode verwenden.
var clone = require('clone');
var a = { foo: { bar: 'baz' } }; // inital value of a
var b = clone(a); // clone a -> b
a.foo.bar = 'foo'; // change a
console.log(a); // { foo: { bar: 'foo' } }
console.log(b); // { foo: { bar: 'baz' } }
(Disclaimer:. Ich bin der Autor der Bibliothek)
Ich weiß, das ist eine alte Post, aber ich dachte, das an die nächste Person eine Hilfe sein, die entlang stolpert.
Solange Sie zuweisen ein Objekt nicht zu irgendetwas es keinen Hinweis im Speicher hält. So ein Objekt machen, die Sie unter anderem Objekte teilen möchten, werden Sie eine Fabrik wie so erstellen müssen:
var a = function(){
return {
father:'zacharias'
};
},
b = a(),
c = a();
c.father = 'johndoe';
alert(b.father);
Cloning
ein Objekt war immer ein Problem in JS, aber es war alles über vor ES6, liste ich verschiedene Arten des Kopierens eines Objekts in JavaScript unten, stellen Sie sich das Objekt unten und möchte eine tiefe Kopie davon haben:
var obj = {a:1, b:2, c:3, d:4};
Es gibt nur wenig Möglichkeiten, um dieses Objekt zu kopieren, ohne den Ursprung zu ändern:
1) ES5 +, eine einfache Funktion Verwenden Sie die Kopie zu tun:
function deepCopyObj(obj) {
if (null == obj || "object" != typeof obj) return obj;
if (obj instanceof Date) {
var copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
if (obj instanceof Array) {
var copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = cloneSO(obj[i]);
}
return copy;
}
if (obj instanceof Object) {
var copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = cloneSO(obj[attr]);
}
return copy;
}
throw new Error("Unable to copy obj this object.");
}
2) ES5 +, mit JSON.parse und JSON.stringify.
var deepCopyObj = JSON.parse(JSON.stringify(obj));
3) AngularJS:
var deepCopyObj = angular.copy(obj);
4) jQuery:
var deepCopyObj = jQuery.extend(true, {}, obj);
5) Underscore.js & Loadash:
var deepCopyObj = _.cloneDeep(obj); //latest version UndescoreJs makes shallow copy
diese Hilfe Hoffnung ...
Wenn Sie verwenden es, die Underscore.js Bibliothek hat eine Klon Methode.
var newObject = _.clone(oldObject);
Deep Kopieren von Objekten in JavaScript (Ich denke, die beste und einfachste)
1. Mit JSON.parse (JSON.stringify (Objekt));
var obj = {
a: 1,
b: {
c: 2
}
}
var newObj = JSON.parse(JSON.stringify(obj));
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } }
2.Using erstellt Methode
function cloneObject(obj) {
var clone = {};
for(var i in obj) {
if(obj[i] != null && typeof(obj[i])=="object")
clone[i] = cloneObject(obj[i]);
else
clone[i] = obj[i];
}
return clone;
}
var obj = {
a: 1,
b: {
c: 2
}
}
var newObj = cloneObject(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } }
3. Mit Lo-Dash _.cloneDeep Link lodash
var obj = {
a: 1,
b: {
c: 2
}
}
var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } }
4. Mit Object.assign () Methode
var obj = {
a: 1,
b: 2
}
var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }
ABER falsch, wenn
var obj = {
a: 1,
b: {
c: 2
}
}
var newObj = Object.assign({}, obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// Note: Properties on the prototype chain and non-enumerable properties cannot be copied.
5.Using Underscore.js _.clone Link Underscore.js
var obj = {
a: 1,
b: 2
}
var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }
ABER falsch, wenn
var obj = {
a: 1,
b: {
c: 2
}
}
var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// (Create a shallow-copied clone of the provided plain object. Any nested objects or arrays will be copied by reference, not duplicated.)
JSBEN.CH Performance Benchmarking Spielplatz 1 ~ 3 http://jsben.ch/KVQLd
Hier ist eine Version von ConroyP Antwort über das funktioniert auch, wenn der Konstruktor Parameter hat erforderlich:
//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
object_create = function(o) {
function F() {}
F.prototype = o;
return new F();
};
}
function deepCopy(obj) {
if(obj == null || typeof(obj) !== 'object'){
return obj;
}
//make sure the returned object has the same prototype as the original
var ret = object_create(obj.constructor.prototype);
for(var key in obj){
ret[key] = deepCopy(obj[key]);
}
return ret;
}
Diese Funktion ist auch in meiner simpleoo Bibliothek.
Edit:
Hier ist eine robustere Version (dank Justin McCandless dies nun zyklische Referenzen als auch unterstützt):
/**
* Deep copy an object (make copies of all its object properties, sub-properties, etc.)
* An improved version of http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
* that doesn't break if the constructor has required parameters
*
* It also borrows some code from http://stackoverflow.com/a/11621004/560114
*/
function deepCopy(src, /* INTERNAL */ _visited, _copiesVisited) {
if(src === null || typeof(src) !== 'object'){
return src;
}
//Honor native/custom clone methods
if(typeof src.clone == 'function'){
return src.clone(true);
}
//Special cases:
//Date
if(src instanceof Date){
return new Date(src.getTime());
}
//RegExp
if(src instanceof RegExp){
return new RegExp(src);
}
//DOM Element
if(src.nodeType && typeof src.cloneNode == 'function'){
return src.cloneNode(true);
}
// Initialize the visited objects arrays if needed.
// This is used to detect cyclic references.
if (_visited === undefined){
_visited = [];
_copiesVisited = [];
}
// Check if this object has already been visited
var i, len = _visited.length;
for (i = 0; i < len; i++) {
// If so, get the copy we already made
if (src === _visited[i]) {
return _copiesVisited[i];
}
}
//Array
if (Object.prototype.toString.call(src) == '[object Array]') {
//[].slice() by itself would soft clone
var ret = src.slice();
//add it to the visited array
_visited.push(src);
_copiesVisited.push(ret);
var i = ret.length;
while (i--) {
ret[i] = deepCopy(ret[i], _visited, _copiesVisited);
}
return ret;
}
//If we've reached here, we have a regular object
//make sure the returned object has the same prototype as the original
var proto = (Object.getPrototypeOf ? Object.getPrototypeOf(src): src.__proto__);
if (!proto) {
proto = src.constructor.prototype; //this line would probably only be reached by very old browsers
}
var dest = object_create(proto);
//add this object to the visited array
_visited.push(src);
_copiesVisited.push(dest);
for (var key in src) {
//Note: this does NOT preserve ES5 property attributes like 'writable', 'enumerable', etc.
//For an example of how this could be modified to do so, see the singleMixin() function
dest[key] = deepCopy(src[key], _visited, _copiesVisited);
}
return dest;
}
//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
object_create = function(o) {
function F() {}
F.prototype = o;
return new F();
};
}
In der folgenden erstellt zwei Instanzen desselben Objekts. Ich fand es und bin mit ihm zur Zeit. Es ist einfach und leicht zu bedienen.
var objToCreate = JSON.parse(JSON.stringify(cloneThis));
Lodash hat eine schöne _.cloneDeep (Wert) Methode:
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
function clone(obj)
{ var clone = {};
clone.prototype = obj.prototype;
for (property in obj) clone[property] = obj[property];
return clone;
}
Crockford schlägt vor (und ich ziehe) mit dieser Funktion:
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
var newObject = object(oldObject);
Es ist kurz und bündig, wie erwartet funktioniert, und Sie brauchen nicht eine Bibliothek.
EDIT:
Dies ist ein polyfill für Object.create
, so können Sie auch diese verwenden.
var newObject = Object.create(oldObject);
Hinweis: Wenn Sie einige dieser verwenden, können Sie Probleme mit einigen Iteration haben die hasOwnProperty
verwenden. Denn create
neues leeres Objekt erstellen, die oldObject
erbt. Aber es ist immer noch nützlich und praktisch für Objekte klonen.
Für exemple wenn oldObject.a = 5;
newObject.a; // is 5
aber:
oldObject.hasOwnProperty(a); // is true
newObject.hasOwnProperty(a); // is false
Shallow Kopie Einzeiler ( ECMAScript 5th edition ):
var origin = { foo : {} };
var copy = Object.keys(origin).reduce(function(c,k){c[k]=origin[k];return c;},{});
console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true
Und flache Kopie Einzeiler ( ECMAScript 6. Ausgabe , 2015):
var origin = { foo : {} };
var copy = Object.assign({}, origin);
console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true
Nur weil ich nicht sehen AngularJS erwähnt und dachte, dass die Leute vielleicht wissen wollen. ..
angular.copy
stellt auch ein Verfahren der tiefen Kopieren von Objekten und Arrays.
Es scheint kein Ideal tiefe Klon Betreiber noch für Array-ähnliche Objekte. Wie Sie den Code unten zeigt, wird John Resig jQuery cloner Arrays mit nicht-numerischen Eigenschaften in Objekte, die keine Arrays sind, und RegDwight JSON cloner die nicht-numerischen Eigenschaften fällt. Die folgenden Tests zeigen diese Punkte auf mehreren Browsern:
function jQueryClone(obj) {
return jQuery.extend(true, {}, obj)
}
function JSONClone(obj) {
return JSON.parse(JSON.stringify(obj))
}
var arrayLikeObj = [[1, "a", "b"], [2, "b", "a"]];
arrayLikeObj.names = ["m", "n", "o"];
var JSONCopy = JSONClone(arrayLikeObj);
var jQueryCopy = jQueryClone(arrayLikeObj);
alert("Is arrayLikeObj an array instance?" + (arrayLikeObj instanceof Array) +
"\nIs the jQueryClone an array instance? " + (jQueryCopy instanceof Array) +
"\nWhat are the arrayLikeObj names? " + arrayLikeObj.names +
"\nAnd what are the JSONClone names? " + JSONCopy.names)
Ich habe zwei gute Antworten je nachdem, ob Ihr Ziel ist es, ein „plain old JavaScript-Objekt“ oder nicht zu klonen.
Lassen Sie sich auch davon ausgehen, dass Ihre Absicht ist es, eine vollständige Klon ohne Prototyp Referenzen zurück zu dem Quellobjekt zu erstellen. Wenn Sie nicht daran interessiert, in einem vollständigen Klon sind, dann können Sie in einigen der anderen Antworten (Crockford Muster), die viele der Object.clone () Routinen verwenden.
Für die guten alte JavaScript-Objekte, eine bewährte und gute Möglichkeit, ein Objekt in der modernen Runtimes zu klonen ist ganz einfach:
var clone = JSON.parse(JSON.stringify(obj));
Beachten Sie, dass das Quellobjekt muss ein reines JSON-Objekt sein. Das heißt, alle seine verschachtelten Eigenschaften müssen Skalare sein (wie boolean, String, Array, Objekt, etc). Alle Funktionen, oder spezielle Objekte wie RegExp oder Datum werden nicht geklont werden.
Ist es leistungsfähig? Oh Ja. Wir haben alle Arten von Klonen Methoden ausprobiert und dies am besten funktioniert. Ich bin sicher, einige ninja eine schnellere Methode heraufbeschwören könnte. Aber ich vermute, wir reden über marginale Gewinne.
Dieser Ansatz ist nur einfach und leicht zu implementieren. Wickeln Sie es in eine Komfortfunktion, und wenn Sie wirklich etwas gewinnen müssen, Squeeze-out, gehen zu einem späteren Zeitpunkt.
Nun, für Nicht-Ebene JavaScript-Objekte, ist es nicht eine wirklich einfache Antwort. In der Tat kann es nicht wegen der Dynamik der JavaScript-Funktionen und inneren Objektzustandes. Tief Klonen eine JSON-Struktur mit Funktionen innerhalb erfordert, dass Sie die Funktionen und ihren inneren Kontext neu zu erstellen. Und Javascript einfach nicht über eine standardisierte Art und Weise, dies zu tun.
Der richtige Weg, dies zu tun, noch einmal, ist über eine bequeme Methode, die Sie in Ihrem Code deklarieren und wiederzuverwenden. Die bequeme Methode kann mit einem gewissen Verständnis für Ihre eigenen Objekte ausgestattet sein, so dass Sie sicherstellen können, um richtig die Grafik innerhalb des neuen Objekts neu zu erstellen.
Wir sind unsere eigenen geschrieben, aber die beste allgemeine Ansatz, den ich habe hier behandelt zu sehen ist:
http://davidwalsh.name/javascript-clone
Dies ist die richtige Idee. Der Autor (David Walsh) hat das Klonen von verallgemeinerten Funktionen Kommentar gesetzt. Dies ist etwas, das Sie je nach Anwendungsfall tun könnten wählen.
Die Grundidee ist, dass Sie die Instanziierung Ihrer Funktionen (oder prototypal Klassen sozusagen) auf einer Pro-Typ Basis spezielle Griff benötigen. Hier ist er ein paar Beispiele für RegExp und Datum versehen.
Dies ist nicht nur Code kurz, aber es ist auch sehr gut lesbar. Es ist ziemlich einfach zu erweitern.
Ist das leistungsfähig? Oh Ja. Da das Ziel ist, eine wahre Tief Kopie Klon zu produzieren, dann sind Sie gehen zu müssen, die Mitglieder des Quellobjektgraphen gehen. Mit diesem Ansatz können Sie zwicken genau, welche untergeordneten Elemente zu behandeln und wie manuell benutzerdefinierte Typen verarbeiten.
Also los Sie. Zwei Ansätze. Beide sind effizient in meiner Ansicht.
Dies ist im Allgemeinen nicht die effizienteste Lösung, aber es tut, was ich brauche. Einfache Testfälle unter ...
function clone(obj, clones) {
// Makes a deep copy of 'obj'. Handles cyclic structures by
// tracking cloned obj's in the 'clones' parameter. Functions
// are included, but not cloned. Functions members are cloned.
var new_obj,
already_cloned,
t = typeof obj,
i = 0,
l,
pair;
clones = clones || [];
if (obj === null) {
return obj;
}
if (t === "object" || t === "function") {
// check to see if we've already cloned obj
for (i = 0, l = clones.length; i < l; i++) {
pair = clones[i];
if (pair[0] === obj) {
already_cloned = pair[1];
break;
}
}
if (already_cloned) {
return already_cloned;
} else {
if (t === "object") { // create new object
new_obj = new obj.constructor();
} else { // Just use functions as is
new_obj = obj;
}
clones.push([obj, new_obj]); // keep track of objects we've cloned
for (key in obj) { // clone object members
if (obj.hasOwnProperty(key)) {
new_obj[key] = clone(obj[key], clones);
}
}
}
}
return new_obj || obj;
}
Zyklische Array-Test ...
a = []
a.push("b", "c", a)
aa = clone(a)
aa === a //=> false
aa[2] === a //=> false
aa[2] === a[2] //=> false
aa[2] === aa //=> true
Funktionstest ...
f = new Function
f.a = a
ff = clone(f)
ff === f //=> true
ff.a === a //=> false
AngularJS
Nun, wenn Sie Winkel verwenden Sie könnten dies auch tun
var newObject = angular.copy(oldObject);
Ich bin nicht einverstanden mit der Antwort mit dem größten Stimmen hier . Ein rekursive Tief Clone ist viel schneller als der JSON.parse (JSON.stringify (obj)) Ansatz erwähnt.
- Jsperf ordnet es die Nummer eins hier: http://jsben.ch/13YKQ
Und hier ist die Funktion zum schnellen Nachschlagen:
function cloneDeep (o) {
let newO
let i
if (typeof o !== 'object') return o
if (!o) return o
if (Object.prototype.toString.apply(o) === '[object Array]') {
newO = []
for (i = 0; i < o.length; i += 1) {
newO[i] = cloneDeep(o[i])
}
return newO
}
newO = {}
for (i in o) {
if (o.hasOwnProperty(i)) {
newO[i] = cloneDeep(o[i])
}
}
return newO
}
// obj target object, vals source object
var setVals = function (obj, vals) {
if (obj && vals) {
for (var x in vals) {
if (vals.hasOwnProperty(x)) {
if (obj[x] && typeof vals[x] === 'object') {
obj[x] = setVals(obj[x], vals[x]);
} else {
obj[x] = vals[x];
}
}
}
}
return obj;
};
Für die Menschen, die die JSON.parse(JSON.stringify(obj))
Version verwenden möchten, ohne jedoch die Date-Objekte zu verlieren, können Sie die zweites Argument von parse
Methode die Strings zurück zum Datum konvertieren:
function clone(obj) {
var regExp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
return JSON.parse(JSON.stringify(x), function(k, v) {
if (typeof v === 'string' && regExp.test(v))
return new Date(v);
return v;
});
}
Hier ist eine umfassendes Klon () Methode, die jedes JavaScript-Objekt klonen. Es behandelt fast alle Fälle:
function clone(src, deep) {
var toString = Object.prototype.toString;
if (!src && typeof src != "object") {
// Any non-object (Boolean, String, Number), null, undefined, NaN
return src;
}
// Honor native/custom clone methods
if (src.clone && toString.call(src.clone) == "[object Function]") {
return src.clone(deep);
}
// DOM elements
if (src.nodeType && toString.call(src.cloneNode) == "[object Function]") {
return src.cloneNode(deep);
}
// Date
if (toString.call(src) == "[object Date]") {
return new Date(src.getTime());
}
// RegExp
if (toString.call(src) == "[object RegExp]") {
return new RegExp(src);
}
// Function
if (toString.call(src) == "[object Function]") {
//Wrap in another method to make sure == is not true;
//Note: Huge performance issue due to closures, comment this :)
return (function(){
src.apply(this, arguments);
});
}
var ret, index;
//Array
if (toString.call(src) == "[object Array]") {
//[].slice(0) would soft clone
ret = src.slice();
if (deep) {
index = ret.length;
while (index--) {
ret[index] = clone(ret[index], true);
}
}
}
//Object
else {
ret = src.constructor ? new src.constructor() : {};
for (var prop in src) {
ret[prop] = deep
? clone(src[prop], true)
: src[prop];
}
}
return ret;
};