Apa cara paling efisien untuk mengkloning objek JavaScript? Saya pernah melihat obj = eval(uneval(o));
sedang digunakan, tetapi itu tidak standar dan hanya didukung oleh Firefox .
Saya sudah melakukan hal-hal seperti obj = JSON.parse(JSON.stringify(o));
tetapi mempertanyakan efisiensinya.
Saya juga melihat fungsi penyalinan rekursif dengan berbagai kekurangan.
Saya terkejut tidak ada solusi kanonik.
javascript
object
clone
jschrab
sumber
sumber
eval()
umumnya merupakan ide yang buruk karena banyak pengoptimal mesin Javascript harus dimatikan ketika berhadapan dengan variabel yang ditetapkan melaluieval
. Hanya dengan memilikieval()
kode Anda dapat menyebabkan kinerja yang lebih buruk.JSON
metode ini akan kehilangan semua jenis Javascript yang tidak memiliki padanan dalam JSON. Misalnya:JSON.parse(JSON.stringify({a:null,b:NaN,c:Infinity,d:undefined,e:function(){},f:Number,g:false}))
akan menghasilkan{a: null, b: null, c: null, g: false}
Jawaban:
Kloning dalam asli
Ini disebut "kloning terstruktur", bekerja secara eksperimental di Node 11 dan yang lebih baru, dan semoga akan mendarat di browser. Lihat jawaban ini untuk lebih jelasnya.
Kloning cepat dengan kehilangan data - JSON.parse / strify
Jika Anda tidak menggunakan
Date
s, fungsi,undefined
,Infinity
, regexps, Maps, Set, gumpalan, FileLists, ImageDatas, Array jarang, Diketik Array atau jenis kompleks lainnya dalam objek Anda, satu kapal yang sangat sederhana untuk klon dalam sebuah objek adalah:JSON.parse(JSON.stringify(object))
Lihat jawaban Corban untuk tolok ukur.
Kloning yang andal menggunakan perpustakaan
Karena objek kloning tidak sepele (tipe kompleks, referensi melingkar, fungsi dll), sebagian besar perpustakaan utama menyediakan fungsi untuk mengkloning objek. Jangan menemukan kembali roda - jika Anda sudah menggunakan perpustakaan, periksa apakah ia memiliki fungsi kloning objek. Sebagai contoh,
cloneDeep
; dapat diimpor secara terpisah melalui modul lodash.clonedeep dan mungkin merupakan pilihan terbaik Anda jika Anda belum menggunakan perpustakaan yang menyediakan fungsi kloning yang mendalamangular.copy
jQuery.extend(true, { }, oldObject)
;.clone()
hanya elemen klon DOMES6
Untuk kelengkapan, perhatikan bahwa ES6 menawarkan dua mekanisme penyalinan dangkal:
Object.assign()
dan sintaksis penyebaran . yang menyalin nilai dari semua properti yang dapat dihitung sendiri dari satu objek ke objek lain. Sebagai contoh:sumber
.clone()
, yang bukan kode yang tepat untuk dijadikan gunakan dalam konteks ini). Sayangnya pertanyaan ini telah melalui begitu banyak revisi sehingga diskusi aslinya tidak lagi terlihat! Harap ikuti saran Corban dan tulis satu lingkaran atau salin properti langsung ke objek baru, jika Anda peduli dengan kecepatan. Atau coba sendiri!Lihat patokan ini: http://jsben.ch/#/bWfk9
Dalam tes saya sebelumnya di mana kecepatan adalah masalah utama yang saya temukan
menjadi cara paling lambat untuk mengkloning suatu objek (itu lebih lambat dari jQuery.extend dengan
deep
flag true 10-20%).jQuery.extend cukup cepat ketika
deep
flag diatur kefalse
(clone dangkal). Ini adalah pilihan yang baik, karena mencakup beberapa logika tambahan untuk validasi tipe dan tidak menyalin properti yang tidak ditentukan, dll., Tetapi ini juga akan sedikit memperlambat Anda.Jika Anda mengetahui struktur objek yang Anda coba klon atau dapat menghindari array bersarang yang mendalam, Anda dapat menulis
for (var i in obj)
loop sederhana untuk mengkloning objek Anda saat memeriksa hasOwnProperty dan itu akan jauh lebih cepat daripada jQuery.Terakhir jika Anda mencoba untuk mengkloning struktur objek yang dikenal dalam loop panas, Anda bisa mendapatkan KINERJA BANYAK BANYAK LAGI dengan hanya menyelaraskan prosedur klon dan secara manual membangun objek.
Mesin jejak JavaScript menyedot optimalisasi
for..in
loop dan memeriksa hasOwnProperty akan memperlambat Anda juga. Mengkloning manual ketika kecepatan adalah keharusan mutlak.Hati-hati menggunakan
JSON.parse(JSON.stringify(obj))
metode padaDate
objek -JSON.stringify(new Date())
mengembalikan representasi string tanggal dalam format ISO, yangJSON.parse()
tidak dikonversi kembali keDate
objek. Lihat jawaban ini untuk lebih jelasnya .Selain itu, harap perhatikan bahwa, setidaknya di Chrome 65, kloning asli bukan cara yang tepat. Menurut JSPerf, melakukan kloning asli dengan membuat fungsi baru hampir 800x lebih lambat daripada menggunakan JSON.stringify yang sangat cepat sepanjang jalan di seluruh papan.
Perbarui untuk ES6
Jika Anda menggunakan Javascript ES6, coba metode asli ini untuk kloning atau salinan dangkal.
sumber
keys
dari Andaobject
, yang memilikifunctions
nilainya, karenaJSON
tidak mendukung fungsi.JSON.parse(JSON.stringify(obj))
Objek Tanggal juga akan mengubah tanggal kembali ke UTC dalam representasi string dalam format ISO8601 .Dengan asumsi bahwa Anda hanya memiliki variabel dan tidak ada fungsi di objek Anda, Anda bisa menggunakan:
sumber
JSON
diimplementasikan dalam kode asli (di sebagian besar browser), ini akan jauh lebih cepat daripada menggunakan solusi penyalinan dalam berbasis javascript lainnya, dan kadang - kadang mungkin lebih cepat daripada teknik penyalinan dangkal berbasis javascript (lihat: jsperf.com/cloning -an-objek / 79 ).JSON.stringify({key: undefined}) //=> "{}"
Date
objek yang disimpan di dalam objek, mengubahnya menjadi bentuk string.Kloning Terstruktur
Standar HTML mencakup algoritma kloning / serialisasi terstruktur internal yang dapat membuat klon objek dalam. Ini masih terbatas pada tipe bawaan tertentu, tetapi selain beberapa jenis yang didukung oleh JSON, ini juga mendukung Tanggal, RegExps, Peta, Set, Blob, FileLists, ImageDatas, Array yang jarang, Array yang Diketik, dan mungkin lebih banyak di masa depan . Itu juga mempertahankan referensi dalam data kloning, memungkinkannya untuk mendukung struktur siklus dan rekursif yang akan menyebabkan kesalahan untuk JSON.
Dukungan di Node.js: Eksperimental 🙂
The
v8
modul Node.js saat ini (per Node 11) memperlihatkan serialisasi API terstruktur secara langsung , tetapi fungsi ini masih ditandai sebagai "eksperimental", dan tunduk pada perubahan atau penghapusan dalam versi masa depan. Jika Anda menggunakan versi yang kompatibel, kloning objek semudah:Dukungan Langsung di Peramban: Mungkin Akhirnya? 😐
Browser saat ini tidak menyediakan antarmuka langsung untuk algoritma kloning terstruktur, tetapi
structuredClone()
fungsi global telah dibahas dalam whatwg / html # 793 di GitHub . Seperti yang diusulkan saat ini, menggunakannya untuk sebagian besar tujuan akan sesederhana:Kecuali jika ini dikirimkan, implementasi klon terstruktur browser hanya diekspos secara tidak langsung.
Pemecahan Masalah Asinkron: Dapat Digunakan. 😕
Cara overhead yang lebih rendah untuk membuat klon terstruktur dengan API yang ada adalah memposting data melalui satu port dari MessageChannels . Port lain akan memancarkan
message
acara dengan klon terstruktur dari terlampir.data
. Sayangnya, mendengarkan acara-acara ini selalu asinkron, dan alternatif sinkron kurang praktis.Contoh Penggunaan:
Solusi Sinkron: Mengerikan! 🤢
Tidak ada opsi yang baik untuk membuat klon terstruktur secara serempak. Berikut adalah beberapa peretasan yang tidak praktis.
history.pushState()
danhistory.replaceState()
keduanya membuat klon terstruktur dari argumen pertama mereka, dan menetapkan nilai ituhistory.state
. Anda dapat menggunakan ini untuk membuat klon terstruktur dari objek apa pun seperti ini:Contoh Penggunaan:
Tampilkan cuplikan kode
Meskipun sinkron, ini bisa sangat lambat. Itu menimbulkan semua overhead yang terkait dengan memanipulasi riwayat browser. Memanggil metode ini berulang kali dapat menyebabkan Chrome menjadi tidak responsif sementara.
The
Notification
konstruktor menciptakan klon terstruktur data yang terkait. Itu juga mencoba untuk menampilkan pemberitahuan browser kepada pengguna, tetapi ini akan gagal secara diam-diam kecuali Anda telah meminta izin pemberitahuan. Jika Anda memiliki izin untuk tujuan lain, kami akan segera menutup pemberitahuan yang kami buat.Contoh Penggunaan:
Tampilkan cuplikan kode
sumber
history.pushState()
danhistory.replaceState()
metode keduanya secara sinkron diaturhistory.state
ke klon terstruktur dari argumen pertama mereka. Agak aneh, tapi berhasil. Saya memperbarui jawaban saya sekarang.Jika tidak ada yang builtin, Anda dapat mencoba:
sumber
Cara efisien untuk mengkloning (bukan deep-clone) suatu objek dalam satu baris kode
Sebuah
Object.assign
metode adalah bagian dari 2015 standar ECMAScript (ES6) dan melakukan apa yang Anda butuhkan.Baca lebih banyak...
The polyfill untuk mendukung browser lama:
sumber
Kode:
Uji:
sumber
var obj = {}
danobj.a = obj
from.constructor
adalahDate
contohnya. Bagaimanaif
tes ketiga akan tercapai ketikaif
tes kedua akan berhasil & menyebabkan fungsi kembali (sejakDate != Object && Date != Array
)?Inilah yang saya gunakan:
sumber
cloneObject({ name: null })
=>{"name":{}}
typeof null > "object"
tetapiObject.keys(null) > TypeError: Requested keys of a value that is not an object.
ubah kondisinya menjadiif(typeof(obj[i])=="object" && obj[i]!=null)
Salinan mendalam menurut kinerja: Diurutkan dari yang terbaik hingga yang terburuk
Menyalin sederetan string atau angka (satu tingkat - tanpa petunjuk referensi):
Ketika array berisi angka dan string - fungsi seperti .slice (), .concat (), .splice (), operator penugasan "=", dan fungsi klon Underscore.js; akan membuat salinan yang dalam dari elemen-elemen array.
Di mana penugasan kembali memiliki kinerja tercepat:
Dan .slice () memiliki kinerja yang lebih baik daripada .concat (), http://jsperf.com/duplicate-array-slice-vs-concat/3
Menyalin dalam-dalam array objek (dua level atau lebih - pointer referensi):
Tulis fungsi khusus (memiliki kinerja lebih cepat dari $ .extend () atau JSON.parse):
Gunakan fungsi utilitas pihak ketiga:
Di mana $ .extend jQuery memiliki kinerja yang lebih baik:
sumber
out[key] = (typeof v === "object" && v !== null) ? copy(v) : v;
sumber
Menyalin objek dalam JavaScript (saya pikir yang terbaik dan paling sederhana)
1. Menggunakan JSON.parse (JSON.stringify (objek));
2.Menggunakan metode yang dibuat
3. Menggunakan Lo-Dash _.cloneDeep Link lodash
4. Menggunakan metode Object.assign ()
TAPI SALAH SAAT
5.Menggunakan tautan Underscore.js _.clone Underscore.js
TAPI SALAH SAAT
JSBEN.CH Play Benchmarking Performance 1 ~ 3 http://jsben.ch/KVQLd
sumber
Object.assign()
tidak melakukan salinan dalamlet data = {title:["one", "two"]}; let tmp = cloneObject(data); tmp.title.pop();
itu melempar:TypeError: tmp.title.pop is not a function
(tentu saja pop () berfungsi dengan baik jika saya hanyado let tmp = data
; tetapi kemudian saya tidak dapat memodifikasi tmp tanpa mempengaruhi data)Ada perpustakaan (disebut "clone") , yang melakukan ini dengan cukup baik. Ini memberikan kloning / penyalinan rekursif paling lengkap dari objek arbitrer yang saya tahu. Ini juga mendukung referensi melingkar, yang belum tercakup oleh jawaban lain, belum.
Anda dapat menemukannya di npm , juga. Ini dapat digunakan untuk browser dan juga Node.js.
Berikut adalah contoh cara menggunakannya:
Instal dengan
atau bungkus dengan Ender .
Anda juga dapat mengunduh kode sumber secara manual.
Kemudian Anda dapat menggunakannya dalam kode sumber Anda.
(Penafian: Saya penulis perpustakaan.)
sumber
JSON.parse(JSON.stringify(obj))
?Cloning
sebuah Object selalu menjadi perhatian di JS, tetapi itu semua tentang sebelum ES6, saya mendaftar berbagai cara menyalin objek dalam JavaScript di bawah ini, bayangkan Anda memiliki Object di bawah ini dan ingin memiliki salinan yang dalam tentang itu:Ada beberapa cara untuk menyalin objek ini, tanpa mengubah asal:
1) ES5 +, Menggunakan fungsi sederhana untuk melakukan penyalinan untuk Anda:
2) ES5 +, menggunakan JSON.parse dan JSON.stringify.
3) AngularJs:
4) jQuery:
5) Garis BawahJs & Loadash:
Semoga bantuan ini ...
sumber
Object.assign
tidak menyalin dalam - dalam. Contoh:var x = { a: { b: "c" } }; var y = Object.assign({}, x); x.a.b = "d"
. Jika ini salinan yang dalam,y.a.b
pasti masihc
, tapi sekarangd
.Saya tahu ini adalah posting lama, tapi saya pikir ini mungkin bisa membantu orang berikutnya yang tersandung.
Selama Anda tidak menetapkan objek untuk apa pun itu tidak memiliki referensi dalam memori. Jadi untuk membuat objek yang ingin Anda bagikan di antara objek lain, Anda harus membuat pabrik seperti:
sumber
const defaultFoo = { a: { b: 123 } };
Anda bisa pergiconst defaultFoo = () => ({ a: { b: 123 } };
dan masalah Anda terpecahkan. Namun, itu sebenarnya bukan jawaban untuk pertanyaan itu. Mungkin lebih masuk akal sebagai komentar pada pertanyaan, bukan jawaban lengkap.Jika Anda menggunakannya, perpustakaan Underscore.js memiliki metode klon .
sumber
.clone(...)
metode perpustakaan utilitas . Setiap perpustakaan utama akan memilikinya, dan jawaban singkat yang tidak terperinci yang diulang tidak berguna bagi kebanyakan pengunjung, yang tidak akan menggunakan perpustakaan itu.Berikut versi jawaban ConroyP di atas yang berfungsi bahkan jika konstruktor memiliki parameter yang diperlukan:
Fungsi ini juga tersedia di perpustakaan simpleoo saya .
Edit:
Ini versi yang lebih kuat (terima kasih kepada Justin McCandless, ini sekarang mendukung referensi siklik juga):
sumber
Berikut ini membuat dua instance dari objek yang sama. Saya menemukannya dan saya menggunakannya saat ini. Ini sederhana dan mudah digunakan.
sumber
Crockford menyarankan (dan saya lebih suka) menggunakan fungsi ini:
Ini singkat, berfungsi seperti yang diharapkan dan Anda tidak perlu perpustakaan.
EDIT:
Ini untuk polyfill
Object.create
, jadi Anda juga bisa menggunakan ini.CATATAN: Jika Anda menggunakan beberapa dari ini, Anda mungkin memiliki masalah dengan beberapa iterasi yang digunakan
hasOwnProperty
. Karena,create
buat objek kosong baru yang mewarisioldObject
. Tetapi masih bermanfaat dan praktis untuk objek kloning.Contohnya jika
oldObject.a = 5;
tapi:
sumber
var extendObj = function(childObj, parentObj) { var tmpObj = function () {} tmpObj.prototype = parentObj.prototype; childObj.prototype = new tmpObj(); childObj.prototype.constructor = childObj; };
... davidshariff.com/blog/javascript-inheritance-patternsLodash memiliki metode _.cloneDeep (nilai) yang bagus:
sumber
.clone(...)
metode perpustakaan utilitas . Setiap perpustakaan utama akan memilikinya, dan jawaban singkat yang tidak terperinci yang diulang tidak berguna bagi kebanyakan pengunjung, yang tidak akan menggunakan perpustakaan itu._.merge({}, objA)
. Jika saja Lodash tidak bermutasi objek di tempat pertama makaclone
fungsi tidak akan diperlukan.sumber
Salinan satu baris dangkal ( ECMAScript edisi ke-5 ):
Dan salin dangkal satu-liner ( ECMAScript edisi 6 , 2015):
sumber
Object.keys
melompati properti yang tidak dapat dihitung dan diwariskan. Juga, itu kehilangan deskriptor properti dengan melakukan penugasan langsung.Hanya karena saya tidak melihat AngularJS disebutkan dan berpikir bahwa orang mungkin ingin tahu ...
angular.copy
juga menyediakan metode menyalin objek dan array.sumber
angular.extend({},obj);
jQuery.extend
danangular.extend
keduanya adalah salinan dangkal.angular.copy
adalah salinan yang dalam.Tampaknya belum ada operator deep clone yang ideal untuk objek mirip array. Seperti yang diilustrasikan oleh kode di bawah ini, cloner jQuery John Resig mengubah array dengan properti non-numerik menjadi objek yang bukan array, dan klon JSON RegDwight menjatuhkan properti non-numerik. Tes berikut menggambarkan poin-poin ini pada beberapa browser:
sumber
Saya punya dua jawaban yang baik tergantung pada apakah tujuan Anda adalah untuk mengkloning "objek JavaScript lama biasa" atau tidak.
Mari kita juga berasumsi bahwa niat Anda adalah untuk membuat klon lengkap tanpa referensi prototipe kembali ke objek sumber. Jika Anda tidak tertarik pada klon lengkap, maka Anda dapat menggunakan banyak rutin Object.clone () yang disediakan di beberapa jawaban lain (pola Crockford).
Untuk objek JavaScript lama biasa, cara yang baik dan benar untuk mengkloning objek dalam runtimes modern cukup sederhana:
Perhatikan bahwa objek sumber harus objek JSON murni. Dengan kata lain, semua properti bersarangnya harus skalar (seperti boolean, string, array, objek, dll). Fungsi atau objek khusus seperti RegExp atau Date tidak akan dikloning.
Apakah efisien? Heck ya. Kami telah mencoba semua jenis metode kloning dan ini bekerja paling baik. Saya yakin beberapa ninja bisa menyulap metode yang lebih cepat. Tapi saya curiga kita berbicara tentang keuntungan marjinal.
Pendekatan ini sederhana dan mudah diimplementasikan. Bungkus menjadi fungsi kenyamanan dan jika Anda benar-benar perlu memeras keuntungan, lakukan di lain waktu.
Sekarang, untuk objek JavaScript yang tidak jelas, tidak ada jawaban yang sangat sederhana. Bahkan, tidak mungkin ada karena sifat dinamis fungsi JavaScript dan keadaan objek batin. Kloning mendalam struktur JSON dengan fungsi di dalamnya mengharuskan Anda membuat ulang fungsi-fungsi itu dan konteks dalamnya. Dan JavaScript sama sekali tidak memiliki cara standar untuk melakukan itu.
Cara yang benar untuk melakukan ini, sekali lagi, adalah melalui metode kenyamanan yang Anda nyatakan dan gunakan kembali dalam kode Anda. Metode kenyamanan dapat diberkahi dengan beberapa pemahaman tentang objek Anda sendiri sehingga Anda dapat memastikan untuk membuat kembali grafik dengan benar dalam objek baru.
Kami menulis sendiri, tetapi pendekatan umum terbaik yang pernah saya lihat dibahas di sini:
http://davidwalsh.name/javascript-clone
Ini ide yang tepat. Penulis (David Walsh) telah berkomentar kloning fungsi umum. Ini adalah sesuatu yang mungkin Anda pilih untuk dilakukan, tergantung pada kasus penggunaan Anda.
Gagasan utamanya adalah bahwa Anda perlu menangani instantiasi fungsi Anda secara khusus (atau kelas prototipal), berdasarkan tipe per. Di sini, dia memberikan beberapa contoh untuk RegExp dan Tanggal.
Tidak hanya kode ini singkat, tetapi juga sangat mudah dibaca. Cukup mudah untuk diperpanjang.
Apakah ini efisien? Heck ya. Mengingat tujuannya adalah untuk menghasilkan tiruan salinan-dalam yang benar, maka Anda harus memandu anggota-anggota dari grafik objek sumber. Dengan pendekatan ini, Anda dapat mengubah anggota anak mana yang harus dirawat dan cara menangani jenis kustom secara manual.
Jadi begitulah. Dua pendekatan. Keduanya efisien menurut saya.
sumber
Ini biasanya bukan solusi yang paling efisien, tetapi ia melakukan apa yang saya butuhkan. Kotak uji sederhana di bawah ...
Tes susunan siklik ...
Tes fungsi ...
sumber
AngularJS
Nah, jika Anda menggunakan sudut Anda bisa melakukan ini juga
sumber
Saya tidak setuju dengan jawaban dengan suara terbesar di sini . Sebuah Rekursif Jauh Clone adalah jauh lebih cepat daripada JSON.parse (JSON.stringify (obj)) pendekatan disebutkan.
Dan inilah fungsi untuk referensi cepat:
sumber
if(o instanceof Date) return new Date(o.valueOf());
setelah memeriksa null`sumber
Hanya ketika Anda bisa menggunakan ECMAScript 6 atau transpiler .
Fitur:
Kode:
sumber
Berikut adalah metode clone () komprehensif yang dapat mengkloning objek JavaScript apa pun. Ini menangani hampir semua kasus:
sumber