Structured Clone: Jurus Rahasia Deep Copy Objek Kompleks di JavaScript Modern
1. Pendahuluan
Sebagai developer web, kita sering berhadapan dengan objek dan data. Baik itu state di aplikasi frontend React/Vue/Angular, konfigurasi di backend Node.js, atau data yang diambil dari API, kita perlu memanipulasi dan mengelola data tersebut dengan aman. Salah satu operasi yang terlihat sederhana namun seringkali menimbulkan bug aneh adalah “deep copy” objek.
Berapa banyak dari kita yang pernah menggunakan JSON.parse(JSON.stringify(obj)) untuk menduplikasi objek? 🙋♂️ Saya yakin banyak. Jurus ini memang cepat dan mudah di awal, tetapi ia punya banyak keterbatasan yang bisa jadi bom waktu di aplikasi Anda. Data seperti Date, RegExp, Map, Set, atau bahkan fungsi dan DOM nodes akan hilang atau terkorupsi dengan metode ini.
Untungnya, JavaScript modern hadir dengan solusi yang lebih elegan dan robust: structuredClone(). Fungsi global yang diperkenalkan di ES2022 ini adalah jurus rahasia yang mungkin belum banyak developer tahu, tapi sangat powerful untuk deep copy objek kompleks dengan benar dan aman.
Artikel ini akan membahas tuntas mengapa structuredClone() adalah pilihan terbaik untuk deep copy di JavaScript, apa saja yang bisa ia lakukan, dan bagaimana menggunakannya secara efektif dalam proyek Anda. Mari kita selami!
2. Apa itu Deep Copy dan Kenapa Penting?
Sebelum masuk ke structuredClone(), mari kita pahami dulu perbedaan antara shallow copy dan deep copy.
📌 Shallow Copy (Salinan Dangkal) Ketika Anda melakukan shallow copy, Anda membuat salinan objek atau array baru, tetapi elemen-elemen di dalamnya (jika berupa objek atau array lain) masih merujuk ke lokasi memori yang sama dengan objek aslinya.
Contoh:
const original = {
nama: "Budi",
alamat: {
jalan: "Jl. Merdeka",
nomor: 10
},
hobi: ["coding", "membaca"]
};
const shallowCopy = { ...original }; // Shallow copy menggunakan spread operator
shallowCopy.nama = "Andi";
shallowCopy.alamat.nomor = 12; // ⚠️ Ini akan mengubah original.alamat.nomor juga!
shallowCopy.hobi.push("bersepeda"); // ⚠️ Ini akan mengubah original.hobi juga!
console.log(original.nama); // Budi
console.log(original.alamat.nomor); // 12 (Berubah!)
console.log(original.hobi); // ["coding", "membaca", "bersepeda"] (Berubah!)
Seperti yang Anda lihat, perubahan pada alamat.nomor dan hobi di shallowCopy ikut memengaruhi original. Ini bisa menyebabkan bug yang sulit dilacak, terutama dalam aplikasi dengan manajemen state yang kompleks.
🎯 Deep Copy (Salinan Mendalam) Deep copy, di sisi lain, membuat salinan objek yang sepenuhnya independen dari objek aslinya. Semua objek bersarang (nested objects) juga disalin secara rekursif, sehingga tidak ada referensi yang berbagi memori.
💡 Kenapa Deep Copy Penting?
- Immutability: Dalam paradigma pemrograman fungsional atau ketika bekerja dengan state management (seperti Redux, Zustand, Recoil), menjaga data tetap immutable (tidak dapat diubah) adalah praktik terbaik. Deep copy memungkinkan Anda mengubah salinan tanpa memengaruhi data asli.
- Mencegah Side Effects: Mengubah objek yang dibagikan antar bagian kode dapat menyebabkan side effects yang tidak diinginkan dan sulit diprediksi. Deep copy memastikan setiap bagian kode bekerja dengan salinannya sendiri.
- Testing: Untuk pengujian unit, Anda seringkali perlu membuat salinan data uji yang dapat dimanipulasi tanpa merusak data asli atau memengaruhi pengujian lain.
3. Keterbatasan JSON.parse(JSON.stringify()): Bom Waktu yang Tersembunyi
Sebelum structuredClone(), JSON.parse(JSON.stringify(obj)) adalah jurus andalan banyak developer untuk deep copy. Mari kita lihat mengapa ini adalah solusi yang tidak ideal:
const userProfile = {
id: 1,
username: "johndoe",
lastLogin: new Date(), // Objek Date
settings: {
theme: "dark",
notifications: true
},
greet: function() { console.log("Hello!"); }, // Fungsi
status: undefined, // undefined
regExp: /abc/g, // Objek RegExp
dataMap: new Map([['key', 'value']]), // Objek Map
dataArrayBuffer: new ArrayBuffer(8) // Objek ArrayBuffer
};
const jsonCopy = JSON.parse(JSON.stringify(userProfile));
console.log(jsonCopy.lastLogin); // String, bukan objek Date!
console.log(jsonCopy.greet); // undefined (Fungsi hilang!)
console.log(jsonCopy.status); // undefined (Benar, tapi jika ada property lain yang hilang?)
console.log(jsonCopy.regExp); // {} (Objek kosong, bukan RegExp!)
console.log(jsonCopy.dataMap); // {} (Objek kosong!)
console.log(jsonCopy.dataArrayBuffer); // {} (Objek kosong!)
❌ Masalah utama JSON.parse(JSON.stringify()):
- Kehilangan Fungsi: Fungsi tidak dapat diserialisasi ke JSON, jadi akan hilang.
- Kehilangan
undefined: Properti dengan nilaiundefinedakan diabaikan. - Transformasi
Date: ObjekDateakan diubah menjadi string ISO 8601, bukan objekDateyang valid. - Transformasi
RegExp: ObjekRegExpakan menjadi objek kosong{}. - Transformasi
MapdanSet: ObjekMapdanSetakan menjadi objek kosong{}. - Transformasi
ArrayBuffer,TypedArray,Blob,File,ImageData: Objek-objek ini juga akan menjadi objek kosong{}. - Tidak bisa menyalin DOM Nodes.
- Tidak bisa menyalin objek dengan referensi sirkular (circular references). Ini akan menyebabkan error
Converting circular structure to JSON.
Jelas, JSON.parse(JSON.stringify()) bukanlah solusi deep copy yang universal dan aman.
4. Memperkenalkan structuredClone(): Sang Penyelamat
structuredClone() adalah fungsi global yang dirancang khusus untuk membuat deep copy dari nilai JavaScript. Ia menggunakan algoritma structured cloning, mekanisme yang sama yang digunakan oleh Web Workers untuk mengirim pesan antar thread, dan juga oleh IndexedDB untuk menyimpan data.
✅ Sintaks Dasar:
const newObject = structuredClone(originalObject);
Mari kita coba lagi contoh userProfile dengan structuredClone():
const userProfile = {
id: 1,
username: "johndoe",
lastLogin: new Date(),
settings: {
theme: "dark",
notifications: true
},
greet: function() { console.log("Hello!"); }, // Fungsi
status: undefined,
regExp: /abc/g,
dataMap: new Map([['key', 'value']]),
dataArrayBuffer: new ArrayBuffer(8)
};
const structuredCopy = structuredClone(userProfile);
// Perubahan pada salinan tidak memengaruhi asli
structuredCopy.settings.theme = "light";
structuredCopy.dataMap.set('newKey', 'newValue');
console.log(userProfile.settings.theme); // dark (Tidak berubah!)
console.log(userProfile.dataMap.get('newKey')); // undefined (Tidak berubah!)
// Tipe data kompleks tetap terjaga
console.log(structuredCopy.lastLogin instanceof Date); // true
console.log(structuredCopy.regExp instanceof RegExp); // true
console.log(structuredCopy.dataMap instanceof Map); // true
console.log(structuredCopy.dataArrayBuffer instanceof ArrayBuffer); // true
// Fungsi dan undefined tetap memiliki batasan
console.log(structuredCopy.greet); // undefined (Fungsi tetap hilang, ini memang batasan structured cloning)
console.log(structuredCopy.status); // undefined (Status undefined tetap dipertahankan)
⚠️ Penting: Meskipun structuredClone() sangat powerful, ia juga memiliki batasan yang sama dengan algoritma structured cloning secara umum:
- Tidak bisa meng-clone fungsi (
function). Fungsi akan diabaikan atau menghasilkanundefined. - Tidak bisa meng-clone DOM nodes.
- Tidak bisa meng-clone
Errorobject (ini adalah pengecualian dari beberapa implementasi, tapi secara spesifik di browser,Errorobject akan di-clone dengan propertinamedanmessage). - Tidak bisa meng-clone objek dengan properti yang tidak dapat di-serialize (seperti Promise atau WeakMap).
5. Kekuatan structuredClone(): Mengatasi Tipe Data Kompleks
Inilah daftar tipe data yang dapat di-clone dengan structuredClone() yang tidak bisa ditangani oleh JSON.parse(JSON.stringify()):
- Objek Primitif:
Boolean,Number,String,Date,RegExp. - Objek Struktur Data:
Map,Set. - Objek ArrayBuffer dan TypedArray:
ArrayBuffer,Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,BigInt64Array,BigUint64Array. - Objek File dan Blob:
Blob,File,FileList. - Objek Image:
ImageData,ImageBitmap. - Objek WebAssembly:
WebAssembly.Module,WebAssembly.Instance. - Objek lain yang dapat di-transfer:
MessagePort,ReadableStream,WritableStream,TransformStream,DOMException,Error. - Referensi Sirkular:
structuredClone()mampu menangani objek dengan referensi sirkular tanpa error.
const objWithCircularRef = {};
objWithCircularRef.self = objWithCircularRef;
try {
JSON.parse(JSON.stringify(objWithCircularRef));
} catch (e) {
console.log("❌ JSON.stringify gagal karena referensi sirkular:", e.message);
}
const clonedCircular = structuredClone(objWithCircularRef);
console.log("✅ structuredClone berhasil meng-clone referensi sirkular.");
console.log(clonedCircular.self === clonedCircular); // true (referensi sirkular dipertahankan dalam salinan)
Ini adalah game changer untuk deep copy, karena Anda tidak perlu khawatir lagi tentang detail implementasi atau tipe data aneh yang bisa merusak salinan Anda.
6. Tips dan Best Practices Penggunaan structuredClone()
- Gunakan untuk Deep Copy Umum: Kapan pun Anda perlu salinan objek yang sepenuhnya independen, terutama jika objek tersebut mungkin berisi tipe data kompleks atau referensi sirkular,
structuredClone()adalah pilihan utama. - Pahami Batasannya: Ingat bahwa fungsi dan DOM nodes tidak akan di-clone. Jika Anda perlu meng-clone fungsi, Anda mungkin perlu strategi lain (misalnya, menyalinnya secara manual atau mendefinisikannya ulang).
- Performa: Untuk objek yang sangat besar atau sering di-clone, pertimbangkan implikasi performanya. Meskipun
structuredClone()sangat efisien, ia tetap melakukan iterasi rekursif. Untuk skenario khusus di Web Workers, ada opsitransferyang bisa mengoptimalkan performa dengan “memindahkan” kepemilikan data daripada menyalinnya, namun ini lebih kompleks dan spesifik untuk komunikasi antar-thread. Untuk deep copy murni di main thread,structuredClone()sudah sangat baik. - Dukungan Browser:
structuredClone()didukung luas di browser modern (Chrome 98+, Firefox 94+, Safari 15.4+, Edge 98+). Untuk lingkungan lama, Anda mungkin perlu polyfill atau solusi alternatif (misalnya, library sepertilodash.clonedeep).
// Contoh penggunaan dalam konteks state management (pseudo-code React)
function reducer(state, action) {
switch (action.type) {
case 'UPDATE_USER_SETTINGS':
const newSettings = structuredClone(state.user.settings);
newSettings.theme = action.payload.theme;
return {
...state,
user: {
...state.user,
settings: newSettings
}
};
default:
return state;
}
}
Dalam contoh di atas, kita memastikan newSettings adalah salinan yang sepenuhnya terpisah, sehingga perubahan tidak akan memutasi state asli secara langsung, menjaga prinsip immutability.
Kesimpulan
structuredClone() adalah tambahan yang sangat berharga untuk toolkit JavaScript modern. Ia menyelesaikan masalah deep copy objek kompleks dengan cara yang robust, efisien, dan mendukung berbagai tipe data yang sebelumnya sulit ditangani. Dengan memahami kekuatan dan batasannya, Anda bisa menulis kode yang lebih aman, prediktif, dan mudah di-maintain.
Lupakan JSON.parse(JSON.stringify()) untuk deep copy objek kompleks Anda. Saatnya beralih ke structuredClone() dan nikmati kemudahan serta keandalan yang ditawarkannya!
🔗 Baca Juga
- Immutability: Fondasi Data yang Andal, Prediktif, dan Berkinerja Tinggi di Aplikasi Web Modern
- Mengoptimalkan Komunikasi Web Workers: Memahami Structured Clone, Transferable Objects, dan SharedArrayBuffer untuk Performa Maksimal
- Memilih Strategi State Management yang Tepat untuk Aplikasi Web Modern: Panduan Pragmatis