for...of adalah bentuk perulangan modern yang mengambil isi koleksi satu per satu tanpa mengelola indeks. Topik ini mempraktikkannya atas array, string, Map, dan Set, lalu menjelaskan secara ringkas istilah iterable yang menjadi syaratnya. Bagian akhir membandingkan for...in dengan for...of, dua pernyataan yang mirip bentuknya tetapi berbeda tujuan, serta menutup dengan entries() dan spread untuk kebutuhan praktis.
for…of pada array dan string#
Pernyataan for...of mendeklarasikan satu variabel untuk menampung satu butir, lalu menelusuri koleksinya dari awal sampai habis. Tidak ada penghitung yang dirawat, tidak ada kondisi yang ditulis, dan tidak ada peluang meleset satu langkah. Bentuknya paling nyaman ketika isinya yang dibutuhkan, bukan posisinya.
const belanja = ["beras", "telur", "susu", "kopi"];
for (const butir of belanja) {
console.log(`- ${butir}`);
}- beras
- telur
- susu
- kopiSetiap putaran menyalin satu butir ke variabel butir, dan urutannya mengikuti urutan array. Kata const pada deklarasinya wajar dipakai karena setiap butir tidak diubah di dalam badan. Bila butir perlu diubah, pendekatan yang umum adalah menulis hasilnya ke array baru, bukan menimpa variabel penampungnya.
String juga bisa ditelusuri dengan for...of, dan setiap putaran memberikan satu karakter. Contoh berikut menghitung huruf vokal pada sebuah kata dengan memanfaatkan sifat itu.
const kata = "durian";
let vokal = 0;
for (const huruf of kata) {
if ("aiueo".includes(huruf)) {
vokal += 1;
}
}
console.log(`"${kata}": ${vokal} huruf vokal dari ${kata.length} huruf`);"durian": 3 huruf vokal dari 6 hurufKata durian memuat enam karakter, tiga di antaranya vokal, dan setiap karakter dibandingkan dengan isi string "aiueo". Metode includes() pada string memeriksa keberadaan satu karakter, sehingga badan perulangannya pendek. Untuk kata yang memuat karakter khusus, penelusuran per karakter dengan for...of mengikuti aturan Unicode, sedangkan nilai length bisa berbeda untuk karakter tertentu.
Map dan Set dalam for…of#
Map menyimpan pasangan kunci-nilai, dan for...of atas Map memberikan pasangan tersebut sebagai array dua elemen. Untuk menampungnya dengan rapi, gunakan destructuring, yakni membongkar isi array ke beberapa variabel sekaligus dalam satu baris. Pola [kunci, nilai] pada kepala for...of inilah yang dipakai di hampir setiap penelusuran Map.
// Rekap daftar belanja pribadi
const belanja = ["beras", "telur", "susu", "kopi", "beras"];
const harga = new Map([
["beras", 68000],
["telur", 29000],
["susu", 18500],
["kopi", 24000],
]);
let total = 0;
for (const [butir, hargaSatuan] of harga) {
total += hargaSatuan;
console.log(`${butir}: Rp${hargaSatuan}`);
}
console.log(`Total: Rp${total}`);
const butirUnik = new Set(belanja);
console.log(`Tercatat ${belanja.length} butir, uniknya ${butirUnik.size} butir`);beras: Rp68000
telur: Rp29000
susu: Rp18500
kopi: Rp24000
Total: Rp139500
Tercatat 5 butir, uniknya 4 butirSetiap baris keluaran berasal dari satu entri Map yang dibongkar menjadi butir dan hargaSatuan, sehingga penjumlahan total berjalan bersamaan dengan pencetakan. Set di baris terakhir bertindak sebagai penyaring keunikan, karena butir beras yang tertulis dua kali pada daftar hanya terhitung sekali. Ukuran Set terbaca dari properti size, berbeda dengan array yang memakai length. Pola destructuring yang sama berlaku untuk Map berisi tipe nilai apa pun, sehingga Map tetap bisa ditelusuri meskipun nilainya tidak ikut dijumlahkan.
Protokol iterable secara ringkas#
Tidak semua nilai bisa ditelusuri for...of. Syaratnya satu, nilai itu harus iterable, yakni menyediakan mekanisme bawaan untuk menghasilkan butirnya satu per satu. Array, string, Map, dan Set memenuhinya, sedangkan object biasa tidak. Mekanisme itu dibangun di atas simbol Symbol.iterator, yang cukup diketahui namanya di tahap ini karena pembuatan iterable sendiri jarang dibutuhkan.
const stok = { beras: 2, telur: 1 };
try {
for (const butir of stok) {
console.log(butir);
}
} catch (err) {
console.log(err.message);
}stok is not iterablePesan errornya menjelaskan persis apa yang kurang, yaitu stok tidak iterable. Error itu dilempar sebelum satu putaran pun berjalan, karena pengecekannya terjadi saat for...of dimulai. Penelusuran object memang memakai jalur yang berbeda, yaitu for...in, seperti dibahas pada bagian berikut.
for…in vs for…of#
Pernyataan for...in dan for...of mirip bentuknya, tetapi hasilnya berbeda. for...in menelusuri kunci, yaitu nama properti pada object, sedangkan for...of menelusuri isi koleksi yang iterable. Masing-masing punya kegunaan sendiri, dan kekeliruan terjadi ketika keduanya ditukar.
const stok = { beras: 2, telur: 1, susu: 3 };
for (const nama in stok) {
console.log(`${nama}: ${stok[nama]}`);
}beras: 2
telur: 1
susu: 3Pada object di atas, for...in memberikan nama properti satu per satu, dan nilainya diambil lewat stok[nama] dengan akses bracket. Bentuk inilah yang paling sering ditemui untuk object. Persoalan muncul ketika for...in dipakai pada array, sebagaimana diperagakan berikut ini.
const daftar = ["beras", "telur", "susu"];
// keliru: for...in pada array memberikan indeks dalam bentuk teks
for (const kunci in daftar) {
console.log(`for...in -> ${kunci} (${typeof kunci})`);
}
// tepat: for...of memberikan isinya
for (const isi of daftar) {
console.log(`for...of -> ${isi}`);
}for...in -> 0 (string)
for...in -> 1 (string)
for...in -> 2 (string)
for...of -> beras
for...of -> telur
for...of -> susuPada array, for...in memberikan indeks 0, 1, dan 2 dalam bentuk string, bukan isinya. Tipe string itu tercetak pada keluaran, dan kunci berupa teks berbahaya ketika dipakai untuk aritmetika atau perbandingan ketat. Selain itu, for...in juga ikut menelusuri properti tambahan yang menempel pada array, sesuatu yang jarang diinginkan. Karena itu, gunakan for...of untuk array dan for...in untuk object.
Perbedaan ini paling mudah terasa ketika dicoba langsung. Demo berikut menyediakan dua data, yaitu array dan object, beserta dua tombol. Pilih salah satu data, tekan salah satu tombol, lalu bandingkan apa yang keluar. Kombinasi for...of pada object akan langsung menampilkan pesan error yang sama dengan contoh sebelumnya.
entries() dan spread#
Dua perkakas kecil melengkapi for...of pada keadaan yang sering muncul. Yang pertama adalah entries() pada array, yang menyediakan pasangan indeks dan isi sekaligus. Dibongkar dengan destructuring, pola ini menggantikan for klasik ketika nomor urut dibutuhkan tanpa mengelola penghitung sendiri.
const daftar = ["beras", "telur", "susu"];
for (const [i, nama] of daftar.entries()) {
console.log(`${i + 1}. ${nama}`);
}1. beras
2. telur
3. susuIndeks dari entries() berupa angka sungguhan, sehingga penambahan i + 1 menghasilkan nomor urut yang benar. Untuk object, pasangan kunci-nilai disediakan oleh Object.entries(), sehingga object juga bisa ditelusuri lewat for...of dengan destructuring yang sama.
const stok = { beras: 2, telur: 1, susu: 3 };
for (const [nama, banyak] of Object.entries(stok)) {
console.log(`${nama} x${banyak}`);
}beras x2
telur x1
susu x3Perkakas kedua adalah spread, yakni membentangkan isi koleksi dengan tiga titik. Di dalam kepala for...of, spread menyatukan beberapa koleksi menjadi satu urutan dalam satu ekspresi, tanpa variabel perantara yang harus diberi nama.
const pagi = ["beras", "telur"];
const siang = ["susu", "kopi"];
for (const butir of [...pagi, ...siang]) {
console.log(`- ${butir}`);
}- beras
- telur
- susu
- kopiKesalahan umum#
- Memakai
for...ofpada object biasa. Object tidak iterable, sehingga perulangannya langsung memunculkan TypeError sebelum satu putaran pun berjalan. Gunakanfor...inuntuk kunci atauObject.entries()untuk pasangan kunci-nilai. - Mengubah koleksi yang sedang ditelusuri. Menambah elemen di tengah penelusuran memperpanjang perulangan, dan hasil akhirnya sulit diprediksi. Susun dulu daftar perubahannya ke wadah terpisah, lalu terapkan setelah perulangan selesai.
- Mengabaikan tipe kunci Map. Kunci Map dibandingkan dengan
===, sehingga kunci"1"berupa string dan1berupa angka tersimpan sebagai dua entri berbeda. Tentukan satu tipe kunci sejak awal agar penelusurannya konsisten.
Ringkasan#
for...ofmenelusuri isi koleksi iterable seperti array, string, Map, dan Set, tanpa penghitung maupun kondisi yang dirawat sendiri.- Entri Map dibongkar dengan destructuring
[kunci, nilai], sedangkan Set menyaring butir ganda dan diukur dengansize. for...inmenelusuri kunci object, dan pada array memberikan indeks berupa string, sehingga array tetap ditelusuri denganfor...of.entries()memberikan pasangan indeks-isi pada array dan kunci-nilai pada object lewatObject.entries(), sedangkan spread menggabungkan beberapa koleksi dengan tiga titik.
Referensi#
- for…of — MDN — dokumentasi resmi for…of beserta contoh atas array, string, dan Map.
- for…in — MDN — dokumentasi for…in beserta catatan pemakaiannya pada array.
- Iterables — javascript.info — penjelasan protokol iterable dan Symbol.iterator dengan contoh bertahap.