Bei den meisten Lösungen im Internet gibt es mehrere Probleme. Deshalb habe ich mich für ein Follow-up entschieden, das beinhaltet, warum die akzeptierte Antwort nicht akzeptiert werden sollte.
Ausgangssituation
Ich möchte ein Javascript mit all seinen Kindern und ihren Kindern und so weiter tief kopierenObject
. Da ich aber nicht Art eines normalen Entwickler bin, meine Object
hat normale properties
, circular structures
und sogarnested objects
.
Also lasst uns ein circular structure
und ein nested object
erstes erstellen .
function Circ() {
this.me = this;
}
function Nested(y) {
this.y = y;
}
Lassen Sie uns alles in einem Object
Namen zusammenbringen a
.
var a = {
x: 'a',
circ: new Circ(),
nested: new Nested('a')
};
Als nächstes wollen wir a
in eine Variable mit dem Namen kopieren b
und sie mutieren.
var b = a;
b.x = 'b';
b.nested.y = 'b';
Sie wissen, was hier passiert ist, denn wenn nicht, würden Sie nicht einmal auf diese großartige Frage stoß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"
}
}
Lassen Sie uns nun eine Lösung finden.
JSON
Der erste Versuch, den ich versuchte, war mit JSON
.
var b = JSON.parse( JSON.stringify( a ) );
b.x = 'b';
b.nested.y = 'b';
Verschwenden Sie nicht zu viel Zeit damit 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, oder? Es ist eine rekursive Kopie des Objekts und behandelt auch andere Typen Date
, aber das war keine Voraussetzung.
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 mit meinem Kollegen gestritten hatte, fragte mich mein Chef, was passiert sei, und er fand nach einigem googeln eine einfache Lösung . 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 funktioniert 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, es hat mit der verschachtelten Struktur im Inneren nicht funktioniert.
Polyfill für die native Lösung
Es gibt eine Polyfüllung für Object.create
den älteren Browser, genau wie für den IE 8. Sie wird von Mozilla empfohlen, ist natürlich nicht perfekt und führt zum gleichen Problem wie die 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 den Bereich F
außerhalb des Bereichs platziert, damit wir einen Blick darauf werfen können, was instanceof
uns sagt.
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
Gleiches Problem wie bei der nativen Lösung , jedoch etwas schlechtere Ausgabe.
die bessere (aber nicht perfekte) Lösung
Beim Stöbern fand ich eine ähnliche Frage ( in Javascript, wenn ich eine tiefe Kopie durchführe, wie vermeide ich einen Zyklus, weil eine Eigenschaft "dies" ist? ) Zu dieser Frage , 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';
Und schauen wir uns die Ausgabe an ...
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 stimmen überein, aber es gibt noch einige kleinere Probleme, einschließlich des Änderns instance
von nested
und circ
zu Object
.
Die Struktur von Bäumen, die sich ein Blatt teilen, wird nicht kopiert, sondern zu zwei unabhängigen Blättern:
[Object] [Object]
/ \ / \
/ \ / \
|/_ _\| |/_ _\|
[Object] [Object] ===> [Object] [Object]
\ / | |
\ / | |
_\| |/_ \|/ \|/
[Object] [Object] [Object]
Fazit
Die letzte Lösung mit Rekursion und Cache ist möglicherweise nicht die beste, aber es handelt sich um eine echte Kopie des Objekts. Es behandelt einfach properties
, circular structures
und nested object
, aber es wird vermasseln die Instanz von ihnen während klonen.
jsfiddle