Bei den meisten Lösungen im Internet gibt es mehrere Probleme. Daher habe ich mich entschlossen, einen Nachtrag zu verfassen, in dem ich darlege, warum die akzeptierte Antwort nicht akzeptiert werden sollte.
Ausgangssituation
Quiero Deep-Copy ein Javascript Object
mit all seinen Kindern und deren Kindern und so weiter. Aber da ich kein normaler Entwickler bin, ist mein Object
tiene normal properties
, circular structures
und sogar nested objects
.
Erstellen wir also eine circular structure
und eine nested object
Erstens.
function Circ() {
this.me = this;
}
function Nested(y) {
this.y = y;
}
Bringen wir alles zusammen in einem Object
namens a
.
var a = {
x: 'a',
circ: new Circ(),
nested: new Nested('a')
};
Als nächstes wollen wir kopieren a
in eine Variable namens b
und mutieren sie.
var b = a;
b.x = 'b';
b.nested.y = 'b';
Sie wissen, was hier passiert ist, denn sonst wären Sie gar nicht auf diese tolle Frage gestoßen.
console.log(a, b);
a --> Object {
x: "b",
circ: Circ {
me: Circ { ... }
},
nested: Nested {
y: "b"
}
}
b --> Object {
x: "b",
circ: Circ {
me: Circ { ... }
},
nested: Nested {
y: "b"
}
}
Jetzt müssen wir eine Lösung finden.
JSON
Der erste Versuch, den ich unternahm, war die Verwendung von JSON
.
var b = JSON.parse( JSON.stringify( a ) );
b.x = 'b';
b.nested.y = 'b';
Verschwenden Sie nicht zu viel Zeit damit, Sie werden TypeError: Converting circular structure to JSON
.
Rekursive Kopie (die akzeptierte "Antwort")
Werfen wir einen Blick auf die akzeptierte Antwort.
function cloneSO(obj) {
// Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;
// Handle Date
if (obj instanceof Date) {
var copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
// Handle Array
if (obj instanceof Array) {
var copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = cloneSO(obj[i]);
}
return copy;
}
// Handle Object
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! Its type isn't supported.");
}
Sieht gut aus, nicht wahr? Es ist eine rekursive Kopie des Objekts und verarbeitet auch andere Typen, wie Date
aber das war keine Bedingung.
var b = cloneSO(a);
b.x = 'b';
b.nested.y = 'b';
Rekursion und circular structures
funktioniert nicht gut zusammen... RangeError: Maximum call stack size exceeded
native Lösung
Nachdem ich mich mit meinem Kollegen gestritten hatte, fragte mein Chef uns, was passiert sei, und er fand eine einfache Lösung Lösung nach einigem Googeln. Es heißt Object.create
.
var b = Object.create(a);
b.x = 'b';
b.nested.y = 'b';
Diese Lösung wurde vor einiger Zeit zu Javascript hinzugefügt und behandelt sogar circular structure
.
console.log(a, b);
a --> Object {
x: "a",
circ: Circ {
me: Circ { ... }
},
nested: Nested {
y: "b"
}
}
b --> Object {
x: "b",
circ: Circ {
me: Circ { ... }
},
nested: Nested {
y: "b"
}
}
... und Sie sehen, dass es mit der verschachtelten Struktur darin nicht funktioniert hat.
Polyfill für die native Lösung
Es gibt ein Polyfill für Object.create
in älteren Browsern wie dem IE 8. Es ist so etwas wie eine Empfehlung von Mozilla, und natürlich ist es nicht perfekt und führt zu dem gleichen Problem wie der native Lösung .
function F() {};
function clonePF(o) {
F.prototype = o;
return new F();
}
var b = clonePF(a);
b.x = 'b';
b.nested.y = 'b';
Ich habe die F
außerhalb des Geltungsbereichs, damit wir einen Blick darauf werfen können, was instanceof
sagt uns.
console.log(a, b);
a --> Object {
x: "a",
circ: Circ {
me: Circ { ... }
},
nested: Nested {
y: "b"
}
}
b --> F {
x: "b",
circ: Circ {
me: Circ { ... }
},
nested: Nested {
y: "b"
}
}
console.log(typeof a, typeof b);
a --> object
b --> object
console.log(a instanceof Object, b instanceof Object);
a --> true
b --> true
console.log(a instanceof F, b instanceof F);
a --> false
b --> true
Das gleiche Problem wie bei der native Lösung aber eine etwas schlechtere Leistung.
die bessere (aber nicht perfekte) Lösung
Beim Stöbern habe ich eine ähnliche Frage gefunden ( In Javascript, bei der Durchführung einer tiefen Kopie, wie kann ich vermeiden, einen Zyklus, aufgrund einer Eigenschaft, die "this"? ) zu diesem, aber mit einer viel besseren Lösung.
function cloneDR(o) {
const gdcc = "__getDeepCircularCopy__";
if (o !== Object(o)) {
return o; // primitive value
}
var set = gdcc in o,
cache = o[gdcc],
result;
if (set && typeof cache == "function") {
return cache();
}
// else
o[gdcc] = function() { return result; }; // overwrite
if (o instanceof Array) {
result = [];
for (var i=0; i<o.length; i++) {
result[i] = cloneDR(o[i]);
}
} else {
result = {};
for (var prop in o)
if (prop != gdcc)
result[prop] = cloneDR(o[prop]);
else if (set)
result[prop] = cloneDR(cache);
}
if (set) {
o[gdcc] = cache; // reset
} else {
delete o[gdcc]; // unset again
}
return result;
}
var b = cloneDR(a);
b.x = 'b';
b.nested.y = 'b';
Werfen wir einen Blick auf die Ausgabe...
console.log(a, b);
a --> Object {
x: "a",
circ: Object {
me: Object { ... }
},
nested: Object {
y: "a"
}
}
b --> Object {
x: "b",
circ: Object {
me: Object { ... }
},
nested: Object {
y: "b"
}
}
console.log(typeof a, typeof b);
a --> object
b --> object
console.log(a instanceof Object, b instanceof Object);
a --> true
b --> true
console.log(a instanceof F, b instanceof F);
a --> false
b --> false
Die Anforderungen sind angepasst, aber es gibt noch einige kleinere Probleme, wie die Änderung der instance
von nested
y circ
a Object
.
Die Struktur von Bäumen, die sich ein Blatt teilen, wird nicht kopiert, sie werden zu zwei unabhängigen Blättern:
[Object] [Object]
/ \ / \
/ \ / \
|/_ _\| |/_ _\|
[Object] [Object] ===> [Object] [Object]
\ / | |
\ / | |
_\| |/_ \|/ \|/
[Object] [Object] [Object]
Schlussfolgerung
Die letzte Lösung, die eine Rekursion und einen Cache verwendet, ist vielleicht nicht die beste, aber sie ist eine real Deep-Copy des Objekts. Es behandelt einfache properties
, circular structures
y nested object
aber es wird die Instanz von ihnen beim Klonen durcheinander bringen.
jsfiddle
35 Stimmen
Siehe diese Frage: stackoverflow.com/questions/122102/
0 Stimmen
Unterstützen Sie auf jeden Fall @Niyaz! Shortlink: tinyurl.com/JSCopyObject
290 Stimmen
Für JSON verwende ich
mObj=JSON.parse(JSON.stringify(jsonObject));
79 Stimmen
Ich verstehe wirklich nicht, warum niemand vorschlägt.
Object.create(o)
dass es alles tut, was der Autor verlangt?5 Stimmen
@froginvasion Wahrscheinlich, weil es im IE8 und darunter nicht unterstützt wird.
4 Stimmen
@Wynand Das ist überhaupt kein gutes Argument. Es gibt eine Menge Polyfills für
Object.create
die zum Beispiel gefunden werden können: refheap.com/900061 Stimmen
@froginvasion In Ordnung, obwohl ich argumentieren könnte, dass der Auftraggeber eine "elegante" [ sic und nicht jeder würde notwendigerweise zustimmen, dass die Verwendung eines Polyfills dieser Beschreibung entspricht. Ich habe nur versucht, Ihnen Folgendes mitzuteilen a möglicher Grund dafür
Object.create
wurde nicht vorgeschlagen (falls dies der Fall ist), weshalb ich "wahrscheinlich" schrieb. Unabhängig davon bin ich für Polyfills und habe mir vor kurzem Folgendes angesehen diese eine, die übrigens ähnlich wie Ihr Schnipsel ist. Wenn Sie ein besseres Feedback wünschen, könnten Sie Ihren Vorschlag auch als Antwort hinzufügen.2 Stimmen
Object.create eignet sich hervorragend, wenn Sie den Prototyp Ihres Objekts kopieren möchten.
59 Stimmen
var x = { deep: { key: 1 } }; var y = Object.create(x); x.deep.key = 2;
Nachdem Sie dies getan haben,y.deep.key
wird auch 2 sein, daher kann Object.create NICHT zum Klonen verwendet werden...1 Stimmen
@froginvasion - weil es nicht einmal für flache Objekte funktioniert? (Chrome 45). Du bekommst einfach
__proto__
nicht ein Klon...2 Stimmen
Object.create
bekommt man nicht__proto__
wird ein neues Objekt erzeugt, dessen Prototyp auf das Argument verweist, wodurch eine Kette entsteht. Ich stimme zu, dass es sich vielleicht nicht um einen echten "Klon" im traditionellen Sinne handelt, aber je nach SituationObject.create
ist viel konsistenter als die verschiedenen Klontechniken, die im Folgenden verwendet werden. developer.mozilla.org/de-US/docs/Web/JavaScript/Reference/1 Stimmen
Wie wäre es mit
var newObj = (function(){ return oldObj; }());
23 Stimmen
@r3wt das wird nicht funktionieren... Bitte posten Sie erst, nachdem Sie einen grundlegenden Test der Lösung durchgeführt haben...
2 Stimmen
Hier ein Benchmark von verschiedenen Lösungen: jsben.ch/#/bWfk9
0 Stimmen
@RubenStolk warum kann OP das Objekt nicht in eine neue Variable speichern?
var copyobject=object
??4 Stimmen
@Mahi Dadurch wird das Objekt nicht kopiert. Es wird eine neue Variable erstellt, die auf das gleiche Objekt verweist.
0 Stimmen
@LordLoh. Was ist, wenn Sie in einer Javascript-Umgebung sind, in der Sie keine Bibliotheken wie JSON importieren können. Apples Quartz Composer Editor zum Beispiel, nicht sicher über Adobe CC Scripting.
1 Stimmen
developer.mozilla.org/de-US/docs/Web/JavaScript/Reference/ - Ich habe das gerade mit dem Chrome-Entwickler-Terminal ausprobiert -
oj1={}; oj1.p1=222; oj2={}; Object.assign(oj2,oj1); oj2.p1=555; oj1.p1;
- wird dies zeigen222
Auch hier wird geantwortet: stackoverflow.com/a/36177142/4821760 Stimmen
@LordLoh. Anscheinend kann ich JSON.parse und JSON.stringify doch innerhalb von QC aufrufen. Core Funktionen aufrufen. Auch habe ich dieses Polyfill auf der Referenzseite und Links zu erweiterten Polyfills JSON2 und JSON3 gefunden: developer.mozilla.org/de-US/docs/Web/JavaScript/Reference/
3 Stimmen
{...original}
6 Stimmen
Wenn Sie Object.create() vorschlagen, dann wissen Sie sehr wenig über JS. Mit Object.create wird das Objekt X nicht geklont. Y = Object.create(X) tut dies und nichts weiter: Es wird ein neues Objekt Y mit X als Prototyp erstellt. Das Objekt X wird zum Prototyp von Objekt Y. Das ist nicht das, was Sie wollen. Ganz und gar nicht. Da die Methoden und Daten von X nicht nach Y kopiert werden, sondern referenziert werden, enthält Y eine Referenz auf die Daten und Methoden von X. Wie falsch kann man liegen!?
0 Stimmen
Kürzlich beantwortete es über aquí
0 Stimmen
Ver developer.mozilla.org's JavaScript/Referenz von assign#Deep_Clone .
0 Stimmen
Dadurch sollte eine Kopie erstellt werden:
let y = {...x};
0 Stimmen
Sie können die Clone-Methode von Lodash oder den Spread-Operator verwenden let x = {} let y = {irgendein Wert} x = {...x, ...y}
0 Stimmen
stackoverflow.com/questions/38416020/