Spread dan rest

Menyalin, menggabungkan, dan mengumpulkan nilai dengan tiga titik.

Daftar isi

Tiga titik ... adalah satu sintaks dengan dua peran yang berlawanan arah. Sebagai spread, ia membentangkan isi array atau object menjadi elemen dan properti yang terpisah, sedangkan sebagai rest, ia mengumpulkan sisa nilai menjadi satu array atau object baru. Topik ini membahas kedua peran itu pada array dan object, rest pada destructuring dan parameter fungsi, serta perbedaan salinan dangkal dan salinan dalam pada penyalinan dengan spread. Semua contohnya berjalan di Node.js dan memakai data daftar kelas serta resep masakan yang mudah dihitung ulang.

Spread pada array#

Spread pada array menuliskan isi array ke posisi tempat tiga titiknya berada, seolah elemen satu per satu ditulis ulang di situ. Karena itu, array baru dapat disalin dengan [...asal], dua array dapat digabung dengan [...a, ...b], dan elemen baru dapat diselipkan pada posisi mana pun tanpa metode tambahan. Hasilnya selalu array baru, sehingga array asalnya tidak tersentuh.

js gabung-daftar.js
// Spread tiga titik: membentangkan elemen array ke dalam array lain.
const kelasA = ["Ani", "Budi"];
const kelasB = ["Citra", "Dewi"];

const gabungan = [...kelasA, ...kelasB];
const daftarBaru = ["Eko", ...kelasA, "Fajar"];

console.log(gabungan);
console.log(daftarBaru);
text Keluaran
[ 'Ani', 'Budi', 'Citra', 'Dewi' ]
[ 'Eko', 'Ani', 'Budi', 'Fajar' ]

gabungan dan daftarBaru adalah array yang baru dibuat, sedangkan kelasA dan kelasB tidak berubah sama sekali. Salinan dengan spread juga memutus hubungan antar array, sehingga penambahan elemen pada salinan tidak terlihat oleh array asalnya. Sifat ini menjadikan spread cara bawaan yang nyaman untuk menyalin array, tanpa perulangan dan tanpa metode tambahan. Pola selip pada daftarBaru menggantikan kombinasi concat dan slice yang lebih panjang pada kode lama, dengan hasil yang sama persis.

Spread pada object#

Spread juga bekerja pada object literal, dan aturannya sama, yaitu seluruh properti terbentangkan ke dalam kurung kurawal yang menampungnya. Properti yang ditulis setelah spread menimpa properti dengan nama sama dari hasil spread sebelumnya, sehingga urutan penulisan menentukan nilai akhirnya. Inilah cara paling umum menghasilkan variasi data dari satu object dasar tanpa mengubah dasarnya.

js Contoh: spread object dan penimpaan properti
// Spread pada object: properti yang ditulis di belakang menimpa yang di depan.
const resepDasar = { nama: "Sayur Sop", porsi: 2, pedas: false };

const versiKeluarga = { ...resepDasar, porsi: 6 };
const versiPedas = { ...versiKeluarga, pedas: true };

console.log(versiKeluarga);
console.log(versiPedas);
text Keluaran
{ nama: 'Sayur Sop', porsi: 6, pedas: false }
{ nama: 'Sayur Sop', porsi: 6, pedas: true }

versiKeluarga mengubah porsi menjadi enam, sedangkan versiPedas mewarisi perubahan itu sekaligus mengubah pedas menjadi true. Object resepDasar tetap utuh karena kedua turunannya dibangun dari salinannya, dan setiap spread menghasilkan object baru sehingga pola berantai seperti pada contoh tidak pernah merusak versi sebelumnya. Properti tambahan yang tidak ada pada object dasarnya pun boleh disertakan, misalnya catatan khusus pada satu versi. Urutan penulisan juga menentukan arah penimpaan, { porsi: 6, ...resepDasar } justru menghasilkan porsi 2 karena properti hasil spread ditulis terakhir dan menimpa nilai yang sudah ada. Bila rantai variasinya memanjang, beberapa konstanta bernama lebih mudah dibaca daripada satu baris yang menumpuk banyak penimpaan.

Rest pada destructuring#

Rest adalah sisi kebalikan spread, yaitu tiga titik di depan nama variabel pada sisi kiri destructuring, dan tugasnya mengumpulkan sisanya. Pada pembongkaran array, rest menampung elemen yang belum diambil menjadi array baru. Pada pembongkaran object, rest menampung properti yang belum diambil menjadi object baru. Variabel rest selalu diletakkan paling akhir, dan penempatan lain ditolak dengan error sintaks.

js Contoh: rest pada destructuring array dan object
// Rest pada destructuring: mengumpulkan sisa elemen dan sisa properti.
const nilaiUjian = [88, 92, 79, 95];
const [tertinggi, ...sisaNilai] = nilaiUjian;

console.log(tertinggi);
console.log(sisaNilai);

const { nama, ...dataLainnya } = { nama: "Citra", kelas: "11 IPA 2", absen: 7 };
console.log(nama);
console.log(dataLainnya);
text Keluaran
88
[ 92, 79, 95 ]
Citra
{ kelas: '11 IPA 2', absen: 7 }

tertinggi diambil dari posisi pertama, sedangkan sisaNilai memuat tiga nilai berikutnya sebagai array sungguhan yang boleh diindeks dan dihitung length-nya. Rest menghasilkan struktur baru yang berdiri sendiri, sehingga pengubahan isi rest tidak pernah menyentuh array atau object pemiliknya. Pada object, dataLainnya memuat kelas dan absen tanpa properti nama, dan isinya menyertakan seluruh properti yang tidak disebut di pola. Nama variabel rest bebas dipilih, dan konvensi yang lazim memakai kata sisa, cadangan, atau lainnya sesuai maknanya. Rest memberi nama pada bagian yang tidak diminati, sehingga tidak ada data yang hilang diam-diam saat sebagian isinya diambil.

Rest pada parameter fungsi#

Peran rest yang ketiga ada pada daftar parameter, yaitu mengumpulkan argumen sisa menjadi array saat fungsi dipanggil. Penulisannya sama, tiga titik di depan nama parameter terakhir, dan parameter itu berperilaku sebagai array biasa di dalam fungsi. Perlakuan lengkapnya beserta contohnya ada pada modul fungsi, sehingga di sini cukup diingat bahwa tiga titik pada daftar parameter selalu berarti rest.

Membedakan spread dan rest cukup dengan melihat posisinya. Tiga titik pada sisi kanan penugasan, di dalam pemanggilan fungsi, atau di dalam array dan object literal berarti spread. Tiga titik pada sisi kiri destructuring atau pada daftar parameter berarti rest. Aturan posisi ini juga menjelaskan mengapa rest harus berada di ujung daftar, sebab JavaScript tidak punya cara menentukan sampai kapan argumen harus dikumpulkan bila masih ada parameter setelahnya. Kedua posisi itu tidak pernah bertabrakan dalam satu baris, sehingga maknanya selalu dapat ditentukan dari tempatnya berada. Setelah tiga peran ini dikenal, tiga titik pada kode mana pun tidak lagi membingungkan.

Salinan dangkal versus salinan dalam#

Spread hanya menyalin satu tingkat. Pada array atau object yang bertingkat, elemen dan properti di dalamnya masih berupa referensi yang dibagi bersama dengan asalnya. Salinan semacam ini disebut salinan dangkal (shallow copy), yaitu salinan yang tingkat terluarnya baru sedangkan isinya masih berbagi dengan asalnya. Akibatnya, perubahan pada sarang di dalam salinan juga terlihat pada aslinya.

js Contoh: salinan dangkal masih berbagi array di dalamnya
// Salinan spread hanya satu lapis: array di dalamnya masih dibagi.
const siswa = { nama: "Dewi", nilai: [80, 90] };
const salinan = { ...siswa };

salinan.nilai.push(95);

console.log(salinan.nilai);
console.log(siswa.nilai);
text Keluaran
[ 80, 90, 95 ]
[ 80, 90, 95 ]

Nilai 95 yang ditambahkan pada salinan ternyata muncul juga pada siswa, karena salinan.nilai dan siswa.nilai menunjuk ke array yang sama. Perilaku ini sering tidak disadari, salinan terlihat aman di permukaan tetapi masih berbagi isi di dalamnya. Properti nama aman karena salinannya benar-benar baru, dan hanya sarang di dalamnya yang masih dibagi. Penyalinan dalam memerlukan alat tersendiri, dan structuredClone beserta pembahasannya ada pada modul error dan fitur modern.

Ringkasan#

  • Spread menulis ulang isi array atau object ke tempat tiga titiknya berada, sehingga penyalinan, penggabungan, dan penyisipan menjadi array atau object baru.
  • Pada object, properti yang ditulis setelah spread menimpa properti bernama sama, sehingga urutan penulisan menentukan hasil akhirnya.
  • Rest pada destructuring mengumpulkan sisa elemen atau properti menjadi array atau object baru dan selalu berada di posisi terakhir.
  • Salinan dengan spread hanya dangkal, array dan object di dalamnya masih dibagi, sedangkan salinan dalam membutuhkan alat seperti structuredClone.

Referensi#