menengahobjectreferensiMenengah4 mnt baca

Copy Object: Shallow vs Deep

Menyalin object dengan benar: kenapa assignment biasa bukan salinan, beda shallow copy dan deep copy, dan kapan memakai spread, Object.assign, atau structuredClone. Salah paham soal referensi adalah sumber bug paling licin di JavaScript.

Masalahnya: Assignment Bukan Salinan

Untuk tipe primitif (number, string, boolean), assignment menyalin NILAI. Untuk object dan array, assignment menyalin REFERENSI, yaitu "alamat" menuju data yang sama:

javascript
const a = { nama: "Budi", nilai: [80, 90] };
const b = a; // b menunjuk ke object YANG SAMA, bukan salinan!
b.nama = "Ani";
console.log(a.nama); // "Ani" (ikut berubah!)

Analoginya: a dan b seperti dua kunci menuju satu rumah yang sama. Mengecat lewat kunci b tetap mengubah rumah yang dilihat lewat kunci a. Bug dari kesalahpahaman ini licin karena kodenya terlihat benar dan errornya muncul jauh dari penyebabnya: kamu ubah "salinan", tapi data asli di tempat lain ikut rusak.

Shallow Copy: Satu Level Saja

Shallow copy membuat object BARU, tapi hanya menyalin satu level teratas. Properti primitif jadi milik masing-masing, tapi object/array bertingkat di dalamnya masih berbagi referensi.

javascript
const c = { ...a }; // atau Object.assign({}, a)
c.nama = "Citra";
console.log(a.nama); // "Budi" (aman, level pertama terpisah)

c.nilai.push(100);
console.log(a.nilai); // [80, 90, 100] (MASIH ikut! array dalam tetap satu)

Visualisasinya: rumahnya sudah dua (object luar terpisah), tapi lemari di dalamnya (array nilai) masih satu yang dipakai bersama. Cocok untuk object datar atau saat kamu hanya mengubah properti level pertama, seperti pola update imutabel { ...profil, umur: 18 }.

Contoh nyata yang aman dengan shallow copy:

javascript
const pengaturan = { tema: "dark", bahasa: "id" };
const percobaan = { ...pengaturan, tema: "light" };
console.log(pengaturan.tema); // "dark", asli tidak tersentuh

Deep Copy: structuredClone

Deep copy menyalin SELURUH kedalaman: tiap level menjadi milik masing-masing, tidak ada lagi yang berbagi.

javascript
const d = structuredClone(a);
d.nilai.push(100);
d.nama = "Dewi";
console.log(a.nilai); // [80, 90] (aman total!)
console.log(a.nama);  // "Budi" (aman total!)

structuredClone tersedia di browser modern dan Node 17+. Ia menangani Date, Map, Set, dan bahkan referensi sirkular dengan benar. Alternatif lama yang masih sering terlihat:

javascript
const e = JSON.parse(JSON.stringify(a));

Trik JSON ini hanya untuk data polos: function dan undefined hilang, Date jadi string, Infinity/NaN jadi null, dan referensi sirkular bikin error. Kalau ragu, pilih structuredClone.

Kapan Pakai yang Mana?

  1. Tidak butuh salinan? Pakai referensi biasa, sadar risikonya.
  2. Object datar + update satu level? Spread {...obj}. Cepat dan idiomatis.
  3. Data bertingkat yang akan dimutasi? structuredClone. Sedikit biaya performa demi keamanan.
  4. Object raksasa yang sering disalin? Pikir ulang desainnya; deep copy berulang itu mahal.

Hindari deep copy di loop besar atau render yang sering: salin hanya yang perlu, sedalam yang perlu.

Contoh Nyata: Bug Draf Edit yang Merusak Data Asli

Kasus klasik: halaman profil menampilkan data user, tombol "Edit" membuka form draf yang seharusnya tidak menyentuh data asli sebelum Simpan ditekan.

javascript
const draf = { ...userAktif }; // shallow copy!
draf.nama = "Budi Edited";    // aman, level pertama terpisah
draf.alamat.kota = "Jakarta"; // BAHAYA! alamat asli ikut berubah!

Halaman profil ikut berubah kotanya padahal user belum menekan Simpan, dan tombol "Batal" tidak bisa mengembalikan keadaan. Tidak ada error sama sekali, datanya hanya "berubah sendiri", sehingga bug ini sulit dilacak. Solusinya: karena alamat bertingkat dan akan dimutasi form, pakai structuredClone(userAktif) sejak awal agar draf dan data asli sepenuhnya terpisah.

Jebakan Umum

Membandingkan object dengan ===. Karena === pada object membandingkan referensi (alamat rumah), bukan isi, dua object dengan isi identik tetap tidak sama:

javascript
console.log({ a: 1 } === { a: 1 }); // false!

Untuk membandingkan isi, bandingkan propertinya satu per satu atau pakai trik JSON.stringify(x) === JSON.stringify(y) (dengan catatan urutan key harus sama).

Shallow copy array of object. [...arr] menyalin array-nya, tapi tiap object di dalamnya masih referensi yang sama. Mengubah salin[0].nama tetap merusak asli[0].nama. Untuk array of object yang akan dimutasi, pakai structuredClone(arr) atau arr.map(o => ({...o})) untuk salinan satu level per item.

Catatan teknis: Object.assign(target, ...sumber) menyalin properti enumerable milik sendiri dari sumber ke target dan me-return target. Hati-hati: ia MEMUTASI target, jadi pola amannya selalu Object.assign({}, sumber). Spread object {...obj} pada dasarnya adalah versi sintaks yang lebih ringkas dengan perilaku yang sama.

Live Playground
export default function App(): JSX.Element {
  return <h1>Hello world</h1>
}

Edit kode di kiri, preview kanan ter-update otomatis.

Tantangan

Salinan Aman

Diberi config = {theme: "dark", font: {size: 14}}. Buat salinan, ubah font.size jadi 16 di salinan, pastikan config asli TIDAK berubah. Buktikan dengan console.log. Kebutuhan salinan aman seperti ini muncul terus di aplikasi nyata: menduplikasi draf formulir sebelum user mengedit, membuat snapshot state sebelum simulasi, atau menyalin template dokumen yang akan dikustomisasi per user.

<!doctype html>
<html>
<head>
<meta charset="utf-8" />

</head>
<body>
<h1>Halo JS</h1>
<p>Buka console preview untuk melihat output.</p>
<script src="index.js"></script>
</body>
</html>