Membuat salinan dalam dalam JavaScript menggunakan structuredClone

Platform sekarang dilengkapi denganstructuredClone(), fungsi bawaan untuk penyalinan dalam.

Untuk waktu yang paling lama, Anda harus menggunakan solusi dan library untuk membuat salinan nilai JavaScript yang mendalam. Platform kini dikirimkan dengan structuredClone(), fungsi bawaan untuk penyalinan mendalam.

Dukungan Browser

  • 98
  • 98
  • 94
  • 15,4

Sumber

Salinan dangkal

Menyalin nilai di JavaScript hampir selalu dangkal, tidak seperti mendalam. Artinya, perubahan pada nilai bertingkat yang dalam akan terlihat dalam salinan serta nilai asli.

Salah satu cara untuk membuat shallow copy di JavaScript menggunakan operator penyebaran objek ...:

const myOriginal = {
  someProp: "with a string value",
  anotherProp: {
    withAnotherProp: 1,
    andAnotherProp: true
  }
};

const myShallowCopy = {...myOriginal};

Menambahkan atau mengubah properti secara langsung pada shallow copy hanya akan memengaruhi salinan, bukan asli:

myShallowCopy.aNewProp = "a new value";
console.log(myOriginal.aNewProp)
// ^ logs `undefined`

Namun, menambahkan atau mengubah properti bertingkat yang dalam akan memengaruhi salinan dan properti asli:

myShallowCopy.anotherProp.aNewProp = "a new value";
console.log(myOriginal.anotherProp.aNewProp) 
// ^ logs `a new value`

Ekspresi {...myOriginal} melakukan iterasi pada properti (dapat dihitung) dari myOriginal menggunakan Operator Penyebaran. Kode ini menggunakan nama dan nilai properti, dan menetapkannya satu per satu ke objek kosong yang baru dibuat. Dengan demikian, objek yang dihasilkan bentuknya identik, tetapi dengan salinan daftar properti dan nilainya sendiri. Nilai-nilainya juga disalin, namun nilai primitif yang disebut ditangani secara berbeda oleh nilai JavaScript dibandingkan dengan nilai non-primitif. Untuk mengutip MDN:

Dalam JavaScript, data primitif (nilai primitif, jenis data primitif) adalah data yang bukan objek dan tidak memiliki metode. Ada tujuh jenis data primitif: string, angka, bigint, boolean, tidak terdefinisi, simbol, dan null.

MDN — Primitif

Nilai non-primitif ditangani sebagai references, artinya tindakan menyalin nilai sebenarnya sebetulnya hanya menyalin referensi ke objek dasar yang sama, sehingga menghasilkan perilaku shallow copy.

Salinan mendalam

Kebalikan dari {i>shallow copy<i} adalah {i>deep copy<i}. Algoritma salinan mendalam juga menyalin properti objek satu per satu, tetapi memanggil dirinya sendiri secara rekursif saat menemukan referensi ke objek lain, sehingga membuat salinan objek tersebut juga. Hal ini sangat penting untuk memastikan bahwa dua kode tidak berbagi objek secara tidak sengaja dan tanpa sadar memanipulasi status satu sama lain.

Dahulu tidak ada cara yang mudah atau bagus untuk membuat salinan mendalam dari sebuah nilai di JavaScript. Banyak orang mengandalkan library pihak ketiga seperti fungsi cloneDeep() Lodash. Mungkin solusi yang paling umum untuk masalah ini adalah peretasan berbasis JSON:

const myDeepCopy = JSON.parse(JSON.stringify(myOriginal));

Bahkan, ini adalah solusi yang sangat populer, sehingga V8 mengoptimalkan secara agresif JSON.parse() dan khususnya pola di atas untuk membuatnya secepat mungkin. Dan meskipun cepat, aplikasi ini memiliki beberapa kekurangan dan kabel kabel:

  • Struktur data rekursif: JSON.stringify() akan ditampilkan saat Anda memberinya struktur data rekursif. Hal ini dapat terjadi dengan mudah saat bekerja dengan daftar atau hierarki yang ditautkan.
  • Jenis bawaan: JSON.stringify() akan ditampilkan jika nilai berisi JS lain seperti Map, Set, Date, RegExp, atau ArrayBuffer.
  • Fungsi: JSON.stringify() akan menghapus fungsi secara diam-diam.

Cloning terstruktur

Platform sudah memerlukan kemampuan untuk membuat salinan nilai JavaScript yang mendalam di beberapa tempat: Menyimpan nilai JS di IndexedDB memerlukan beberapa bentuk serialisasi sehingga dapat disimpan di disk dan kemudian dideserialisasi untuk memulihkan nilai JS. Demikian pula, mengirim pesan ke WebWorker melalui postMessage() memerlukan transfer nilai JS dari satu realm JS ke realm JS lainnya. Algoritma yang digunakan untuk hal ini disebut “Structured Clone”, dan hingga saat ini, tidak mudah diakses oleh developer.

Hal itu sekarang telah berubah. Spesifikasi HTML diubah untuk menampilkan fungsi bernama structuredClone() yang secara persis menjalankan algoritma tersebut sebagai sarana bagi developer untuk dengan mudah membuat salinan mendalam dari nilai JavaScript.

const myDeepCopy = structuredClone(myOriginal);

Selesai! Itulah keseluruhan API. Jika Anda ingin mempelajari detailnya lebih lanjut, lihat artikel MDN.

Fitur dan batasan

Cloning terstruktur mengatasi banyak (meskipun tidak semua) kelemahan dari teknik JSON.stringify(). Cloning terstruktur dapat menangani struktur data siklis, mendukung banyak jenis data bawaan, dan umumnya lebih andal dan sering kali lebih cepat.

Namun, cara ini masih memiliki beberapa batasan yang mungkin membuat Anda lengah:

  • Prototipe: Jika menggunakan structuredClone() dengan instance class, Anda akan mendapatkan objek biasa sebagai nilai yang ditampilkan, karena cloning terstruktur akan menghapus rantai prototipe objek.
  • Fungsi: Jika objek Anda berisi fungsi, structuredClone() akan menampilkan pengecualian DataCloneError.
  • Tidak dapat di-clone: Beberapa nilai tidak dapat di-clone secara terstruktur, terutama node Error dan DOM. Tindakan ini akan menyebabkan structuredClone() ditampilkan.

Jika salah satu batasan ini merupakan pemecah transaksi untuk kasus penggunaan Anda, library seperti Lodash masih menyediakan implementasi kustom dari algoritma cloning mendalam lainnya yang mungkin atau mungkin tidak sesuai dengan kasus penggunaan Anda.

Performa

Meskipun saya belum pernah melakukan perbandingan tolok ukur mikro baru, saya melakukan perbandingan pada awal tahun 2018, sebelum structuredClone() diekspos. Saat itu, JSON.parse() adalah opsi tercepat untuk objek yang sangat kecil. Saya berharap hal itu tetap sama. Teknik yang mengandalkan kloning terstruktur (jauh) lebih cepat untuk objek yang lebih besar. Mengingat structuredClone() yang baru tidak mengalami overhead penyalahgunaan API lain dan lebih tangguh daripada JSON.parse(), sebaiknya Anda menjadikannya pendekatan default untuk membuat salinan mendalam.

Kesimpulan

Jika Anda perlu membuat salinan mendalam dari nilai di JS—mungkin karena Anda menggunakan struktur data yang tidak dapat diubah atau Anda ingin memastikan fungsi dapat memanipulasi objek tanpa memengaruhi objek yang asli—Anda tidak perlu lagi mencari solusi atau library. Ekosistem JS sekarang memiliki structuredClone(). Hore.