Saya punya objek x
. Saya ingin menyalinnya sebagai objek y
, sehingga perubahan y
tidak diubah x
. Saya menyadari bahwa menyalin objek yang berasal dari objek JavaScript bawaan akan menghasilkan properti tambahan yang tidak diinginkan. Ini bukan masalah, karena saya menyalin salah satu objek saya sendiri yang dibangun secara literal.
Bagaimana cara saya mengkloning objek JavaScript dengan benar?
javascript
clone
javascript-objects
soundly_typed
sumber
sumber
mObj=JSON.parse(JSON.stringify(jsonObject));
Object.create(o)
, itu melakukan semua yang penulis minta?var x = { deep: { key: 1 } }; var y = Object.create(x); x.deep.key = 2;
Setelah melakukan ini,y.deep.key
juga akan menjadi 2, maka Object.create TIDAK BISA DIGUNAKAN untuk kloning ...Jawaban:
Untuk melakukan ini untuk objek apa pun dalam JavaScript tidak akan mudah atau langsung. Anda akan mengalami masalah salah mengambil atribut dari prototipe objek yang harus dibiarkan dalam prototipe dan tidak disalin ke instance baru. Jika, misalnya, Anda menambahkan
clone
metodeObject.prototype
, seperti yang digambarkan beberapa jawaban, Anda harus melewati atribut itu secara eksplisit. Tetapi bagaimana jika ada metode tambahan lain yang ditambahkanObject.prototype
, atau prototipe perantara lainnya, yang tidak Anda ketahui? Dalam hal ini, Anda akan menyalin atribut yang seharusnya tidak Anda miliki, jadi Anda perlu mendeteksi atribut non-lokal yang tidak terduga denganhasOwnProperty
metode ini.Selain atribut yang tidak dapat dihitung, Anda akan menghadapi masalah yang lebih sulit ketika Anda mencoba menyalin objek yang memiliki properti tersembunyi. Misalnya,
prototype
adalah properti tersembunyi dari suatu fungsi. Juga, prototipe objek direferensikan dengan atribut__proto__
, yang juga tersembunyi, dan tidak akan disalin oleh perulangan for / in iterating pada atribut objek sumber. Saya pikir__proto__
mungkin khusus untuk penerjemah JavaScript Firefox dan mungkin ada sesuatu yang berbeda di browser lain, tetapi Anda mendapatkan gambarnya. Tidak semuanya enumerable. Anda dapat menyalin atribut tersembunyi jika Anda tahu namanya, tetapi saya tidak tahu cara untuk menemukannya secara otomatis.Namun halangan lain dalam pencarian solusi elegan adalah masalah pengaturan pewarisan prototipe dengan benar. Jika prototipe objek sumber Anda adalah
Object
, maka cukup membuat objek umum baru dengan{}
akan bekerja, tetapi jika prototipe sumber tersebut adalah turunan dariObject
, maka Anda akan kehilangan anggota tambahan dari prototipe yang dilewati menggunakanhasOwnProperty
filter, atau yang berada di prototipe, tetapi tidak bisa dihitung pada awalnya. Salah satu solusinya mungkin dengan memanggilconstructor
properti objek sumber untuk mendapatkan objek salinan awal dan kemudian menyalin atribut, tetapi Anda masih tidak akan mendapatkan atribut yang tidak dapat dihitung. Misalnya,Date
objek menyimpan datanya sebagai anggota tersembunyi:String tanggal untuk
d1
akan berada 5 detik di belakang stringd2
. Cara untuk membuat satuDate
sama dengan yang lain adalah dengan memanggilsetTime
metode, tetapi itu khusus untukDate
kelas. Saya tidak berpikir ada solusi umum anti peluru untuk masalah ini, meskipun saya akan senang salah!Ketika saya harus menerapkan dalam umum menyalin Saya akhirnya mengorbankan dengan mengasumsikan bahwa saya hanya akan perlu menyalin polos
Object
,Array
,Date
,String
,Number
, atauBoolean
. 3 jenis terakhir tidak berubah, jadi saya bisa melakukan salinan dangkal dan tidak khawatir tentang itu berubah. Saya selanjutnya berasumsi bahwa elemen apa pun yang terkandung dalamObject
atauArray
juga akan menjadi salah satu dari 6 jenis sederhana dalam daftar itu. Ini dapat dilakukan dengan kode seperti berikut:Fungsi di atas akan bekerja secara memadai untuk 6 tipe sederhana yang saya sebutkan, selama data dalam objek dan array membentuk struktur pohon. Artinya, tidak ada lebih dari satu referensi ke data yang sama di objek. Sebagai contoh:
Ini tidak akan dapat menangani objek JavaScript apa pun, tetapi mungkin cukup untuk banyak tujuan selama Anda tidak menganggap bahwa itu hanya akan berfungsi untuk apa pun yang Anda lemparkan padanya.
sumber
JSON.parse(JSON.stringify([some object]),[some revirer function])
akan menjadi solusi?var cpy = new obj.constructor()
?Jika Anda tidak menggunakan
Date
s, fungsi, tidak terdefinisi, regExp atau Infinity dalam objek Anda, liner yang sangat sederhana adalahJSON.parse(JSON.stringify(object))
:Ini berfungsi untuk semua jenis objek yang berisi objek, array, string, boolean, dan angka.
Lihat juga artikel ini tentang algoritma klon terstruktur dari browser yang digunakan saat memposting pesan ke dan dari pekerja. Ini juga berisi fungsi untuk kloning dalam.
sumber
Dengan jQuery, Anda dapat menyalin dangkal dengan memperluas :
perubahan selanjutnya pada
copiedObject
tidak akan mempengaruhioriginalObject
, dan sebaliknya.Atau untuk membuat salinan yang dalam :
sumber
var copiedObject = jQuery.extend({},originalObject);
jQuery.extend(true, {}, originalObject);
...subsequent changes to the copiedObject will not affect the originalObject, and vice versa...
. Maaf saya benar-benar bingung.Dalam ECMAScript 6 ada metode Object.assign , yang menyalin nilai dari semua properti yang dapat dihitung sendiri dari satu objek ke objek lainnya. Sebagai contoh:
Namun perlu diperhatikan bahwa objek bersarang masih disalin sebagai referensi.
sumber
Object.assign
jalan yang harus ditempuh. Sangat mudah untuk mengisinyaPer MDN :
Object.assign({}, a)
JSON.parse(JSON.stringify(a))
Tidak perlu untuk perpustakaan eksternal tetapi Anda harus memeriksa kompatibilitas browser terlebih dahulu .
sumber
clone
fungsionalitas. lihat lebih lanjut di sini momentjs.com/docs/#/parsing/moment-cloneAda banyak jawaban, tetapi tidak ada yang menyebutkan Object.create dari ECMAScript 5, yang memang tidak memberi Anda salinan persis, tetapi menetapkan sumber sebagai prototipe objek baru.
Jadi, ini bukan jawaban yang tepat untuk pertanyaan itu, tetapi ini adalah solusi satu-baris dan dengan demikian elegan. Dan ini bekerja paling baik untuk 2 kasus:
Contoh:
Mengapa saya menganggap solusi ini lebih unggul? Ini asli, sehingga tidak ada perulangan, tidak ada rekursi. Namun, browser lama akan membutuhkan polyfill.
sumber
var a = {b:'hello',c:{d:'world'}}, b = Object.create(a); a == b /* false */; a.c == b.c /* true */;
Object.create
menunjuk ke properti orang tua melalui referensi. Itu berarti jika nilai properti orang tua berubah, anak juga akan berubah. Ini memiliki beberapa efek samping yang mengejutkan dengan array bersarang dan objek yang dapat menyebabkan bug yang sulit ditemukan dalam kode Anda jika Anda tidak menyadarinya: jsbin.com/EKivInO/2 . Objek yang dikloning adalah objek yang benar-benar baru dan independen yang memiliki properti dan nilai yang sama dengan induknya, tetapi tidak terhubung ke induknya.Cara elegan untuk mengkloning objek Javascript 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
Object.assign
mengambil dua parameter ketikavalue
fungsi di polyfill hanya mengambil satu parameter?arguments
objek sebelumnya.) Saya mengalami kesulitan menemukanObject()
melalui Google ... ini adalah tipografi, bukan?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, sayaObject
punya yang normalproperties
,circular structures
dan bahkannested objects
.Jadi mari kita buat
circular structure
dan yangnested object
pertama.Mari kita kumpulkan semuanya dalam sebuah
Object
namaa
.Selanjutnya, kami ingin menyalin
a
ke variabel bernamab
dan memutasikannya.Anda tahu apa yang terjadi di sini karena jika tidak Anda bahkan tidak akan mendarat pada pertanyaan besar ini.
Sekarang mari kita cari solusinya.
JSON
Upaya pertama yang saya coba gunakan
JSON
.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.
Terlihat bagus, heh? Ini adalah salinan objek berulang dan menangani tipe lain juga, seperti
Date
, tapi itu bukan keharusan.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
.Solusi ini ditambahkan ke Javascript beberapa waktu lalu dan bahkan menangani
circular structure
.... 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 .Saya telah menempatkan di
F
luar ruang lingkup sehingga kita dapat melihat apa yanginstanceof
memberitahu kita.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.
Dan mari kita lihat outputnya ...
Persyaratannya cocok, tetapi masih ada beberapa masalah yang lebih kecil, termasuk mengubah
instance
darinested
dancirc
keObject
.kesimpulan
Solusi terakhir menggunakan rekursi dan cache, mungkin tidak menjadi yang terbaik, tapi itu nyata deep-salinan objek. Menangani sederhana
properties
,circular structures
dannested object
, tetapi itu akan mengacaukan contoh mereka saat kloning.jsfiddle
sumber
Jika Anda setuju dengan salinan yang dangkal, perpustakaan underscore.js memiliki metode klon .
atau Anda dapat memperpanjang seperti
sumber
OK, bayangkan Anda memiliki objek di bawah ini dan Anda ingin mengkloningnya:
atau
jawabannya adalah terutama depeneds yang ECMAScript Anda gunakan, dalam
ES6+
, Anda hanya dapat menggunakanObject.assign
untuk melakukan clone:atau menggunakan operator spread seperti ini:
Tetapi jika Anda menggunakan
ES5
, Anda dapat menggunakan beberapa metode, tetapiJSON.stringify
, pastikan Anda tidak menggunakan sebagian besar data untuk menyalin, tetapi ini bisa menjadi satu cara yang praktis dalam banyak kasus, seperti ini:sumber
big chunk of data
akan disamakan? 100kb? 100MB? Terima kasih!Object.assign
membuat salinan yang dangkal (seperti halnya spread, @Alizera)Salah satu solusi yang tidak berlaku adalah dengan menggunakan pengkodean JSON untuk membuat salinan dalam objek yang tidak memiliki metode anggota. Metodologinya adalah untuk menyandikan JSON objek target Anda, kemudian dengan mendekodekannya, Anda mendapatkan salinan yang Anda cari. Anda dapat memecahkan kode sebanyak yang Anda inginkan untuk membuat salinan sebanyak yang Anda butuhkan.
Tentu saja, fungsi bukan milik JSON, jadi ini hanya berfungsi untuk objek tanpa metode anggota.
Metodologi ini sangat cocok untuk kasus penggunaan saya, karena saya menyimpan gumpalan JSON di penyimpanan nilai kunci, dan ketika mereka diekspos sebagai objek dalam JavaScript API, setiap objek sebenarnya berisi salinan keadaan asli objek sehingga kami dapat menghitung delta setelah penelepon memutasi objek yang terbuka.
sumber
{ 'foo': function() { return 1; } }
adalah objek yang dikonstruksi secara literal.Anda cukup menggunakan properti sebaran untuk menyalin objek tanpa referensi. Tapi hati-hati (lihat komentar), 'copy' hanya pada level objek / array terendah. Properti bersarang masih referensi!
Klon lengkap:
Klon dengan referensi di tingkat kedua:
JavaScript sebenarnya tidak mendukung klon dalam secara asli. Gunakan fungsi utilitas. Misalnya Ramda:
sumber
const first = {a: 'foo', b: 'bar'}; const second = {...{a} = first}
Bagi mereka yang menggunakan AngularJS, ada juga metode langsung untuk kloning atau memperluas objek di perpustakaan ini.
atau
Lebih banyak di dokumentasi angular.copy ...
sumber
Ini adalah fungsi yang bisa Anda gunakan.
sumber
new
. Jawaban yang diterima tidak.Jawaban A.Levy hampir selesai, inilah kontribusi kecil saya: ada cara bagaimana menangani referensi rekursif , lihat baris ini
if(this[attr]==this) copy[attr] = copy;
Jika objeknya adalah elemen XML DOM, kita harus menggunakan cloneNode sebagai gantinya
if(this.cloneNode) return this.cloneNode(true);
Terinspirasi oleh studi lengkap A.Levy dan pendekatan prototyping Calvin, saya menawarkan solusi ini:
Lihat juga catatan Andy Burke dalam jawabannya.
sumber
Date.prototype.clone = function() {return new Date(+this)};
Dari artikel ini: Cara menyalin array dan objek dalam Javascript oleh Brian Huisman:
sumber
var copiedObj = Object.create(obj);
cara yang bagus juga?Di ES-6 Anda cukup menggunakan Object.assign (...). Ex:
Referensi yang baik ada di sini: https://googlechrome.github.io/samples/object-assign-es6/
sumber
let obj = {person: 'Thor Odinson'}; let clone = Object.assign({}, obj); clone.title = "Whazzup";
)obj
properti primitif memang dikloning. Nilai properti yang merupakan objek itu sendiri tidak akan dikloning.Dalam ECMAScript 2018
Sadarilah bahwa objek bersarang masih disalin sebagai referensi.
sumber
const objDeepClone = JSON.parse(JSON.stringify(obj));
Menggunakan Lodash:
sumber
_.cloneDeep(x)
karena pada dasarnya adalah hal yang sama seperti di atas, tetapi membaca lebih baik.Tertarik dalam mengkloning objek sederhana:
JSON.parse(JSON.stringify(json_original));
Sumber: Bagaimana cara menyalin objek JavaScript ke variabel baru BUKAN dengan referensi?
sumber
Anda dapat mengkloning suatu objek dan menghapus referensi apa pun dari yang sebelumnya menggunakan satu baris kode. Cukup lakukan:
Untuk browser / mesin yang saat ini tidak mendukung Object.create Anda dapat menggunakan polyfill ini:
sumber
Object.create(...)
tampaknya pasti cara yang harus ditempuh.Object.hasOwnProperty
? Dengan begitu orang tahu cara mencegah mencari tautan prototipe.text
anggota di obj2. Anda tidak membuat salinan, hanya menunda rantai prototipe ketika anggota tidak ditemukan di obj2.Jawaban baru untuk pertanyaan lama! Jika Anda merasa senang menggunakan ECMAScript 2016 (ES6) dengan Spread Syntax , mudah.
Ini memberikan metode bersih untuk salinan objek yang dangkal. Membuat salinan yang dalam, artinya membuat salinan baru dari setiap nilai di setiap objek bersarang secara rekursif, membutuhkan solusi yang lebih berat di atas.
JavaScript terus berkembang.
sumber
var obj = {'key1': 'value1'};
var array = [...obj]; // TypeError: obj is not iterable
Solusi ES6 jika Anda ingin (dangkal) mengkloning instance kelas dan bukan hanya objek properti.
sumber
let cloned = Object.assign({}, obj)
?Saya pikir ada jawaban yang sederhana dan berhasil. Dalam penyalinan yang dalam ada dua masalah:
Jadi saya pikir salah satu solusi sederhana adalah pertama serialisasi dan deserialize dan kemudian melakukan penugasan untuk menyalin fungsi juga.
Meskipun pertanyaan ini memiliki banyak jawaban, saya harap pertanyaan ini juga membantu.
sumber
cloneDeep
.Untuk salinan dan klon yang dalam, JSON.stringify lalu JSON.parse objek:
sumber
Ini adalah adaptasi dari kode A. Levy untuk juga menangani kloning fungsi dan banyak / referensi siklik - apa artinya ini adalah bahwa jika dua properti dalam pohon yang dikloning adalah referensi dari objek yang sama, pohon objek yang dikloning akan memiliki ini properti menunjuk ke satu dan klon yang sama dari objek yang direferensikan. Ini juga menyelesaikan kasus dependensi siklik yang, jika dibiarkan tidak tertangani, mengarah ke loop tak terbatas. Kompleksitas algoritma adalah O (n)
Beberapa tes cepat
sumber
Saya hanya ingin menambahkan semua
Object.create
solusi di posting ini, bahwa ini tidak berfungsi dengan cara yang diinginkan dengan nodejs.Di Firefox hasil
adalah
{test:"test"}
.Dalam simpul itu
sumber
Object.hasOwnProperty
untuk memeriksa apakah Anda memiliki array atau tidak. Ya ini menambah kompleksitas tambahan untuk berurusan dengan warisan prototipikal.sumber
if(!src && typeof src != "object"){
. Saya pikir seharusnya||
tidak&&
.Karena mindeavor menyatakan bahwa objek yang akan dikloning adalah objek yang 'dikonstruksi secara literal', solusinya mungkin dengan hanya menghasilkan objek beberapa kali daripada mengkloning turunan objek:
sumber
Saya sudah menulis implementasi saya sendiri. Tidak yakin apakah itu dianggap sebagai solusi yang lebih baik:
Berikut ini adalah implementasinya:
sumber