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 Object
dengan semua anak-anak dan anak-anak mereka dan sebagainya. Tapi karena saya bukan jenis pengembang normal, saya Object
punya yang normal properties
, circular structures
dan bahkan nested objects
.
Jadi mari kita buat circular structure
dan yang nested object
pertama.
function Circ() {
this.me = this;
}
function Nested(y) {
this.y = y;
}
Mari kita kumpulkan semuanya dalam sebuah Object
nama a
.
var a = {
x: 'a',
circ: new Circ(),
nested: new Nested('a')
};
Selanjutnya, kami ingin menyalin a
ke variabel bernama b
dan 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 structures
tidak 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.create
di 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 F
luar ruang lingkup sehingga kita dapat melihat apa yang instanceof
memberitahu 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 instance
dari nested
dan circ
ke 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 structures
dan nested object
, tetapi itu akan mengacaukan contoh mereka saat kloning.
jsfiddle