Kloning objek independen dalam JavaScript

Dalam bahasa pemrograman apa pun, ada tipe data yang dideskripsikan oleh pemrogram ke subjek untuk pekerjaan lebih lanjut dan, jika perlu, memprosesnya. JavaScript bukan pengecualian, ia memiliki tipe data primitif ( Number , String , Boolean , Symbol , dll.) Dan referensi ( Array , Object , Function , Maps , Sets , dll.). Perlu dicatat bahwa tipe data primitif tidak dapat diubah - nilainya tidak dapat dimodifikasi, tetapi hanya dapat ditimpa dengan nilai penuh baru, tetapi dengan tipe data referensi, yang terjadi adalah sebaliknya. Sebagai contoh, deklarasikan variabel tipe Number dan Object :

 let num = 5; let obj = { a: 5 }; 

Kami tidak dapat mengubah variabel num , kami hanya dapat menulis ulang nilainya, tetapi kami dapat memodifikasi variabel obj:

 let num = 10; let obj = { a: 5, b: 6 }; 

Seperti yang Anda lihat, dalam kasus pertama kita menimpa nilai variabel, dan yang kedua kita memperluas objek. Dari sini kami menyimpulkan bahwa tipe data primitif tidak dapat diperluas, dan dengan tipe data referensi kami dapat melakukan ini, bahkan dengan pengubah const .

Yang terakhir dapat dibekukan, misalnya, menggunakan Object.freeze(obj) , tetapi topik ini berada di luar cakupan artikel (tautan untuk Object.defineProperty yang ingin tahu, melindungi objek dari perubahan ).

Bagaimana tipe data diteruskan ke fungsi dalam JavaScript? Setiap programmer js mungkin akan dengan mudah menjawab pertanyaan ini, namun demikian, katakanlah: tipe data primitif selalu diteruskan ke fungsi hanya berdasarkan nilai, dan yang direferensikan selalu hanya dengan referensi. Dan di sini dengan yang terakhir, dalam beberapa situasi, masalah muncul. Mari kita lihat sebuah contoh:

 const arr = [0, 1, 2, 3, 4, 5]; console.log("Array: ", arr); // output: Array: [0, 1, 2, 3, 4, 5] 

Dalam hal ini, kami cukup mendeklarasikan array angka dan menampilkannya di konsol. Sekarang kita meneruskannya ke fungsi yang mengembalikan array baru, tetapi dengan penambahan beberapa nilai dalam argumen kedua, ke akhir array baru:

 const arr = [0, 1, 2, 3, 4, 5]; console.log("Old array: ", arr); // "Old array: " [0, 1, 2, 3, 4, 5] const newArr = insertValToArr(arr, 15); console.log("New array: ", newArr); // output: "New array: " [0, 1, 2, 3, 4, 5, 15] console.log("Old array: ", arr); // output: "Old array: " [0, 1, 2, 3, 4, 5, 15] function insertValToArr(arr, val) { const newArr = arr; newArr.push(val); return newArr; } 

Seperti yang bisa kita lihat dari kesimpulan konsol, tidak hanya array baru telah berubah, tetapi juga yang lama. Ini terjadi karena dalam fungsi insertValToArr kami baru saja menetapkan satu array ke const newArr = arr lain const newArr = arr , dan oleh karena itu membuat tautan ke array yang ada dan ketika kami mencoba memodifikasi array baru, itu merujuk ke area memori dari array lama dan, secara kasar, mengubahnya. Dan karena kedua array merujuk ke area memori yang sama, mereka akan memiliki nilai yang sama. Mari kita ubah fungsi kita sehingga tidak bisa mengubah array lama:

 const arr = [0, 1, 2, 3, 4, 5]; const newArr = insertValToArr(arr, 15); console.log("New array: ", newArr); // output: "New array: " [0, 1, 2, 3, 4, 5, 15] console.log("Old array: ", arr); // output: "Old array: " [0, 1, 2, 3, 4, 5] function insertValToArr(arr, val) { const newArr = []; arr.forEach((value, ind) => { newArr[ind] = value}); newArr.push(val); return newArr; } 

Array lama tidak berubah, karena kami menerima masing-masing elemen dan secara individual menetapkan nilai elemen ke elemen array baru. Sekarang yang terakhir memiliki area memori yang terpisah dan jika Anda mengubahnya, maka ini tidak akan mempengaruhi array yang lama. Tetapi semua ini adalah contoh sederhana dan dalam program nyata, kemungkinan besar tidak hanya array satu dimensi yang akan ditemui, tetapi juga dua dimensi, lebih jarang tiga dimensi, dan bahkan lebih jarang empat dimensi. Kebanyakan mereka ditemukan dalam bentuk array asosiatif (tabel hash). Dalam JavaScript, ini adalah objek yang paling sering.

Mari kita lihat metode standar untuk menyalin objek yang disediakan JavaScript - Object.assign () digunakan untuk menyalin nilai semua properti enumerasinya sendiri dari satu atau lebih objek sumber ke objek target. Setelah menyalin, ia mengembalikan objek target. Pertimbangkan itu:

 const obj = { a: 1 }; const newObj = Object.assign({}, obj); console.log(newObj); // output: { a: 1, b: 2 } console.log(obj); // output: { a: 1, b: 2 } 

Dan lagi masalah lama, kita merujuk ke area memori yang sama, yang mengarah pada modifikasi dua objek sekaligus - mengubah satu akan mengubah yang lain. Apa yang harus dilakukan jika kita perlu mendapatkan salinan objek yang kompleks (dengan banyak bercabang) dan pada saat yang sama, mengubah objek, jangan memodifikasi yang lain? Untuk menjawab pertanyaan ini adalah tujuan dari artikel ini. Selanjutnya kita akan mempertimbangkan bagaimana menulis metode kita sendiri untuk kloning mendalam (menyalin) objek dari cabang apa pun. Mari kita mulai ke kode.

Langkah 1: mendeklarasikan dan menginisialisasi objek Z , dan juga membuat output konsol untuk perbandingan sebelum dan setelah kloning:

 const Z = { a: 5, b: { g: 8, y: 9, t: { q: 48 } }, x: 47, l: { f: 85, p: { u: 89, m: 7 }, s: 71 }, r: { h: 9, a: 'test', s: 'test2' } }; console.log('Z object before cloning: ', Z); 

gambar

Langkah 2: tetapkan objek Z ke objek refToZ untuk menunjukkan perbedaan antara penugasan normal dan kloning mendalam:

 const refToZ = Z; 

Langkah 3: tetapkan objek Z objek Y menggunakan fungsi deepClone dan tambahkan properti baru ke objek Y Setelah itu, tampilkan dua objek ini di konsol:

 const Y = deepClone(Z); function deepClone(obj) { const clObj = {}; for(const i in obj) { if (obj[i] instanceof Object) { clObj[i] = deepClone(obj[i]); continue; } clObj[i] = obj[i]; } return clObj; } Y.addnlProp = { fd: 45 }; console.log('Z object after cloning: ', Z); console.log('Y object: ', Y); 

gambar

gambar

Di konsol, kita melihat dengan jelas bahwa mengubah objek Y , menambahkan properti baru, kita tidak mengubah objek Z dan yang terakhir tidak akan memiliki properti addnlProp di tubuhnya.

Langkah 4: ubah properti x , yang ada di tubuh objek Z dan Y dan sekali lagi tampilkan kedua objek di konsol:

 Yx = 76; console.log('Y object: ', Y); console.log('Z object: ', Z); 

gambar

gambar

Dengan mengubah properti yang sama di objek Y , kami tidak memengaruhi properti di tubuh Z

Langkah 5: pada langkah terakhir, untuk perbandingan, kami cukup menambahkan properti addToZ dengan nilai 100 ke objek refToZ dan menampilkan ketiga objek di konsol:

 refToZ.addToZ = 100; console.log('refToZ object: ', refToZ); console.log('Z object: ', Z); console.log('Y object: ', Y); 

gambar

gambar

gambar

Dengan mengubah objek refToZ kami juga mengubah Z , tetapi Y tidak terpengaruh. Dari sini kami menyimpulkan bahwa fungsi kami membuat objek baru yang independen dengan properti dan nilainya dari objek yang ada (kode untuk mengimplementasikan fungsi deepClone dapat ditemukan di CodePen ).

Mari kita membahas implementasi fungsi ini. Yang terakhir menemukan sarang objek, tanpa menyadarinya. Bagaimana dia melakukan ini? Masalahnya adalah bahwa dalam hal ini kita menggunakan algoritma yang terkenal untuk grafik - pencarian mendalam. Objek adalah grafik yang memiliki satu atau banyak cabang, yang pada gilirannya dapat memiliki cabang mereka, dll. Agar kita dapat menemukan semuanya, kita harus masuk ke setiap cabang dan bergerak ke kedalamannya, jadi kita akan menemukan setiap simpul dalam grafik dan mendapatkan nilainya. Pencarian mendalam dapat diimplementasikan dalam 2 cara: rekursi dan menggunakan loop. Yang kedua mungkin berubah menjadi lebih cepat, karena itu tidak akan mengisi tumpukan panggilan, yang pada gilirannya melakukan rekursi. Dalam implementasi fungsi deepClone kami deepClone kami menggunakan kombinasi rekursi dengan loop. Jika Anda ingin membaca buku tentang algoritma, saya menyarankan Anda untuk memulai Aditya Bhargava "Algoritma Grokayem" atau Thomas Kormen "Algoritma: konstruksi dan analisis" yang lebih mendalam.

Untuk meringkas, kami mengingat tipe data dalam JavaScript dan bagaimana mereka diteruskan ke fungsi. Dianggap contoh sederhana kloning independen dari array satu dimensi yang sederhana. Kami mempertimbangkan salah satu implementasi bahasa standar untuk menyalin objek dan sebagai hasilnya menulis fungsi kecil (dalam ukuran) untuk kloning mendalam independen dari objek kompleks. Fungsi serupa dapat menemukan aplikasinya baik di sisi server (Node js), yang lebih mungkin, maupun pada klien. Saya harap artikel ini bermanfaat bagi Anda. Sampai jumpa lagi.

Source: https://habr.com/ru/post/id480786/


All Articles