İnternette çoğu çözümle ilgili çeşitli sorunlar var. Bu yüzden, kabul edilen cevabın neden kabul edilmemesi gerektiğini içeren bir takip yapmaya karar verdim.
başlama durumu
Bir Javascript'i tüm çocukları ve çocukları vb. İle birlikte kopyalamak istiyorum Object
. Ben tür normal bir geliştiricinin olduğuma Ama benim Object
sahiptir , normal properties
, circular structures
hatta nested objects
.
Şimdi bir circular structure
ve bir nested object
ilk oluşturalım .
function Circ() {
this.me = this;
}
function Nested(y) {
this.y = y;
}
Her şeyi bir Object
isimde bir araya getirelim a
.
var a = {
x: 'a',
circ: new Circ(),
nested: new Nested('a')
};
Daha sonra, a
adlı bir değişkene kopyalamak b
ve değiştirmek istiyoruz .
var b = a;
b.x = 'b';
b.nested.y = 'b';
Burada ne olduğunu biliyorsunuz çünkü eğer olmasaydınız bu büyük soruya bile inmezdiniz.
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"
}
}
Şimdi bir çözüm bulalım.
JSON
Denediğim ilk deneme kullanmaktı JSON
.
var b = JSON.parse( JSON.stringify( a ) );
b.x = 'b';
b.nested.y = 'b';
Üzerinde çok fazla zaman kaybetmeyin, elde edersiniz TypeError: Converting circular structure to JSON
.
Özyinelemeli kopya (kabul edilen "cevap")
Kabul edilen cevaba bir bakalım.
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.");
}
İyi görünüyor, değil mi? Nesnenin özyinelemeli bir kopyası ve diğer türleri de ele alıyor Date
, ancak bu bir gereklilik değildi.
var b = cloneSO(a);
b.x = 'b';
b.nested.y = 'b';
Özyineleme ve circular structures
birlikte iyi çalışmıyor ...RangeError: Maximum call stack size exceeded
doğal çözüm
İş arkadaşımla tartıştıktan sonra, patronum bize ne olduğunu sordu ve biraz çalıştıktan sonra basit bir çözüm buldu . Denir Object.create
.
var b = Object.create(a);
b.x = 'b';
b.nested.y = 'b';
Bu çözüm bir süre önce Javascript'e eklendi ve hatta kolları 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"
}
}
... ve görüyorsunuz, içindeki iç içe geçmiş yapı ile çalışmadı.
doğal çözüm için çoklu dolgu
Object.create
Eski tarayıcıda tıpkı IE 8 gibi bir çoklu dolgu var. Mozilla tarafından önerilen gibi bir şey ve elbette mükemmel değil ve yerel çözümle aynı soruna neden oluyor .
function F() {};
function clonePF(o) {
F.prototype = o;
return new F();
}
var b = clonePF(a);
b.x = 'b';
b.nested.y = 'b';
Bize anlattıklarına F
bir göz atabilmek için kapsamın dışına çıktım instanceof
.
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
Yerel çözümle aynı sorun , ancak biraz daha kötü çıktı.
daha iyi (ama mükemmel olmayan) çözüm
Etrafta kazarken, benzer bir soru buldum ( Javascript'te, derin bir kopya yaparken, bir özelliğin "bu" olması nedeniyle bir döngüyü nasıl önleyebilirim? ), Ancak bir şekilde daha iyi bir çözüm.
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';
Çıktıya bir göz atalım ...
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
Gereksinim eşleşti, ancak değiştirme dahil bazı küçük sorunlar hala var instance
içinde nested
ve circ
hiç Object
.
Bir yaprağı paylaşan ağaçların yapısı kopyalanmaz, iki bağımsız yaprak haline gelirler:
[Object] [Object]
/ \ / \
/ \ / \
|/_ _\| |/_ _\|
[Object] [Object] ===> [Object] [Object]
\ / | |
\ / | |
_\| |/_ \|/ \|/
[Object] [Object] [Object]
Sonuç
Özyineleme ve önbellek kullanan son çözüm en iyi olmayabilir, ancak nesnenin gerçek bir derin kopyasıdır. Çok basit kolları properties
, circular structures
ve nested object
, ama olacak bunların örneğine kadar karışıklık klonlama sırasında.
jsfiddle