Ada beberapa masalah dengan sebagian besar solusi di internet. Jadi saya memutuskan untuk membuat tindak lanjut, yang mencakup, mengapa jawaban yang diterima tidak boleh diterima.
situasi mulai
Saya ingin menyalin dalam- dalam Javascript Objectdengan semua anak-anak dan anak-anak mereka dan sebagainya. Tapi karena saya bukan jenis pengembang normal, saya Objectpunya yang normal properties , circular structuresdan bahkan nested objects.
Jadi mari kita buat circular structuredan yang nested objectpertama.
function Circ() {
this.me = this;
}
function Nested(y) {
this.y = y;
}
Mari kita kumpulkan semuanya dalam sebuah Objectnama a.
var a = {
x: 'a',
circ: new Circ(),
nested: new Nested('a')
};
Selanjutnya, kami ingin menyalin ake variabel bernama bdan memutasikannya.
var b = a;
b.x = 'b';
b.nested.y = 'b';
Anda tahu apa yang terjadi di sini karena jika tidak Anda bahkan tidak akan mendarat pada pertanyaan besar ini.
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"
}
}
Sekarang mari kita cari solusinya.
JSON
Upaya pertama yang saya coba gunakan JSON.
var b = JSON.parse( JSON.stringify( a ) );
b.x = 'b';
b.nested.y = 'b';
Jangan buang terlalu banyak waktu untuk itu, Anda akan mendapatkannya TypeError: Converting circular structure to JSON.
Salinan rekursif ("jawaban" yang diterima)
Mari kita lihat jawaban yang diterima.
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.");
}
Terlihat bagus, heh? Ini adalah salinan objek berulang dan menangani tipe lain juga, seperti Date, tapi itu bukan keharusan.
var b = cloneSO(a);
b.x = 'b';
b.nested.y = 'b';
Rekursi dan circular structurestidak bekerja dengan baik bersama ...RangeError: Maximum call stack size exceeded
solusi asli
Setelah berdebat dengan rekan kerja saya, bos saya bertanya kepada kami apa yang terjadi, dan dia menemukan solusi sederhana setelah beberapa googling. Itu disebut Object.create.
var b = Object.create(a);
b.x = 'b';
b.nested.y = 'b';
Solusi ini ditambahkan ke Javascript beberapa waktu lalu dan bahkan menangani 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"
}
}
... dan Anda lihat, itu tidak berfungsi dengan struktur bersarang di dalam.
polyfill untuk solusi asli
Ada polyfill Object.createdi browser lama seperti IE 8. Ini sesuatu seperti yang direkomendasikan oleh Mozilla, dan tentu saja, itu tidak sempurna dan menghasilkan masalah yang sama dengan solusi asli .
function F() {};
function clonePF(o) {
F.prototype = o;
return new F();
}
var b = clonePF(a);
b.x = 'b';
b.nested.y = 'b';
Saya telah menempatkan di Fluar ruang lingkup sehingga kita dapat melihat apa yang instanceofmemberitahu kita.
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
Masalah yang sama dengan solusi asli , tetapi output sedikit lebih buruk.
solusi yang lebih baik (tetapi tidak sempurna)
Ketika menggali sekitar, saya menemukan pertanyaan serupa ( dalam Javascript, ketika melakukan salinan yang dalam, bagaimana saya menghindari siklus, karena properti menjadi "ini"? ) Untuk yang ini, tetapi dengan solusi yang jauh lebih baik.
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';
Dan mari kita lihat outputnya ...
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
Persyaratannya cocok, tetapi masih ada beberapa masalah yang lebih kecil, termasuk mengubah instancedari nesteddan circke Object.
Struktur pohon yang berbagi daun tidak akan disalin, mereka akan menjadi dua daun independen:
[Object] [Object]
/ \ / \
/ \ / \
|/_ _\| |/_ _\|
[Object] [Object] ===> [Object] [Object]
\ / | |
\ / | |
_\| |/_ \|/ \|/
[Object] [Object] [Object]
kesimpulan
Solusi terakhir menggunakan rekursi dan cache, mungkin tidak menjadi yang terbaik, tapi itu nyata deep-salinan objek. Menangani sederhana properties, circular structuresdan nested object, tetapi itu akan mengacaukan contoh mereka saat kloning.
jsfiddle