Dapur kafe Budi menerima antrean pesanan yang harus dicetak satu per satu ke layar dapur. Loop for dengan index bisa melakukannya, tetapi perhatian kita terbagi tiga: variabel penghitung, kondisi berhenti, dan akses antrean[i] harus selalu sinkron — padahal yang kita inginkan hanyalah nilainya satu per satu. Kadang posisi juga dibutuhkan, misalnya untuk menomori pesanan, dan saat itu pula kode mulai ramai.

for...of memindahkan pengelolaan posisi ke bahasa: kita hanya menyebut nama untuk setiap nilai, dan penelusuran berhenti sendiri saat elemen habis. Karena array, string, Map, dan Set mengikuti protokol iterasi dari chapter sebelumnya, satu sintaks yang sama berlaku untuk semuanya. Chapter ini menelusuri antrean dapur dengan for...of, menambahkan posisi lewat entries(), keys(), dan values(), lalu menegaskan bedanya dengan for...in.

Menelusuri Nilai dengan for-of #

Aturan for...of: for (const nilai of iterable) { ... } menjalankan badan loop untuk setiap elemen yang dihasilkan iterator milik iterable. Di balik layar, for...of memanggil [Symbol.iterator]() sekali di awal, lalu memanggil next() berulang sampai done bernilai true — kita menulis variabel nilainya saja. Berkas berikut menelusuri antrean dapur dalam empat bentuk kebutuhan berbeda:

// antrean-pesanan.js
// for-of menelusuri nilai; entries(), keys(), values() menyertakan posisi.

const antrean = ["Es Jeruk", "Kopi Gayo", "Teh Melati"];
for (const pesanan of antrean) {
  console.log(pesanan);
}

for (const [posisi, pesanan] of antrean.entries()) {
  console.log(`${posisi + 1}. ${pesanan}`);
}

for (const posisi of antrean.keys()) {
  console.log(`slot ${posisi}`);
}

// Map dan Set juga iterable, sehingga ikut ditelusuri for-of
const stok = new Map([["Kopi Gayo", 24], ["Teh Melati", 10]]);
for (const [nama, jumlah] of stok) {
  console.log(`${nama}: sisa ${jumlah}`);
}

const belumSelesai = new Set(["P-201", "P-202"]);
for (const nomor of belumSelesai) {
  console.log(`Proses ${nomor}`);
}

// for-in menelusuri nama properti object, bukan nilai
const harga = { kopi: 12000, teh: 8000 };
for (const kunci in harga) {
  console.log(`${kunci}: Rp${harga[kunci]}`);
}

Output yang Dihasilkan:

text
Es Jeruk
Kopi Gayo
Teh Melati
1. Es Jeruk
2. Kopi Gayo
3. Teh Melati
slot 0
slot 1
slot 2
Kopi Gayo: sisa 24
Teh Melati: sisa 10
Proses P-201
Proses P-202
kopi: Rp12000
teh: Rp8000

Mari kita telaah baris-baris pentingnya:

  1. Baris 5–7 (for (const pesanan of antrean)): setiap putaran memberi satu elemen array lengkap ke pesanan — tanpa index, tanpa kondisi, tanpa antrean[i].
  2. Baris 10–12 (antrean.entries()): kebutuhan penomoran terjawab oleh entries(); destructuring [posisi, pesanan] memisahkan index dan elemen dalam satu langkah, dan posisi + 1 mengubah index berbasis nol menjadi nomor manusiawi.
  3. Baris 14–16 (antrean.keys()): bila yang dibutuhkan hanya posisi — misalnya melaporkan slot kosong — keys() menghasilkan index 0, 1, 2 satu per satu.
  4. Baris 19–21 (for (const [nama, jumlah] of stok)): Map ditelusuri tanpa method tambahan karena iterasi bawaannya sudah berupa pasangan; destructuring memisahkan kunci dan nilai.
  5. Baris 24–26 (for (const nomor of belumSelesai)): Set menghasilkan nilai uniknya satu per satu sesuai urutan penyisipan.
  6. Baris 30–32 (for (const kunci in harga)): for-in menghasilkan nama properti berupa string, dan harga[kunci] membaca nilainya — pola ini untuk object, bukan pengganti for-of.

entries(), keys(), dan values() pada Array #

Aturan ketiga method ini: ketiganya mengembalikan iterator — bukan array — sehingga hasilnya langsung ditelusuri for...of dan dibaca satu per satu. entries() menghasilkan pasangan [index, elemen], keys() hanya index, dan values() hanya elemen. Karena array itu sendiri menghasilkan elemen saat ditelusuri, antrean.values() dan antrean memberikan hasil yang sama; values() hadir agar ketiga method tersedia lengkap dan kode terbaca simetris.

Destructuring pada kepala loop di baris 10 sah dilakukan karena setiap hasil entries() adalah array dua elemen. Aturan yang sama berlaku untuk Map di bagian berikutnya: asalkan konsumen menerima pasangan berbentuk array, pattern [a, b] memisahkan keduanya tanpa baris tambahan.

Pilihan antar keduanya mengikuti kebutuhan: butuh nilai saja, tulis for (const pesanan of antrean); butuh posisi dan nilai, gunakan entries() dengan destructuring; butuh posisi saja, gunakan keys(). Pattern ini menggantikan loop index dari section Control Flow pada hampir semua kasus pembacaan — loop index tetap relevan ketika posisi perlu diubah di tengah jalan, misalnya melompati elemen dengan menaikkan penghitung.

for-of pada Map dan Set #

Map dan Set menyediakan method entries(), keys(), dan values() juga. Perbedaannya pada Map: iterasi bawaan sudah berupa pasangan, sehingga stok dan stok.entries() sama-sama menghasilkan [kunci, nilai]. Untuk membaca satu sisi saja, keys() dan values() lebih ringkas:

js
const stok = new Map([["Kopi Gayo", 24], ["Teh Melati", 10]]);
for (const nama of stok.keys()) {
  console.log(nama);
}
for (const jumlah of stok.values()) {
  console.log(jumlah);
}
console.log([...stok.entries()]);

Output yang Dihasilkan:

text
Kopi Gayo
Teh Melati
24
10
[ [ 'Kopi Gayo', 24 ], [ 'Teh Melati', 10 ] ]

Baris terakhir memperlihatkan pola yang sering berguna: spread atas entries() mengumpulkan seluruh pasangan menjadi array of array — bentuk yang siap diproses method array seperti map dan filter. Pada Set, ketiga method memberikan nilai yang sama karena Set hanya menyimpan nilai; belumSelesai.values() setara dengan belumSelesai.keys() dan dengan Set itu sendiri.

Satu kesetaraan yang perlu diingat dari Map: for (const [nama, jumlah] of stok) di berkas utama dan for (const [nama, jumlah] of stok.entries()) adalah dua penulisan untuk penelusuran yang persis sama. Kebiasaan yang sehat: pakai penulisan pendek di kode sehari-hari, dan tuliskan entries() secara eksplisit saat pembaca kode perlu ditegaskan bahwa yang ditelusuri adalah pasangan.

Beda for-in dan for-of #

Keduanya mirip di mata, berbeda di kontrak. for...in menelusuri nama properti (kunci) milik object — selalu berupa string — dan berlaku untuk object biasa. for...of menelusuri nilai milik iterable dan menolak object biasa:

Aspekfor-infor-of
Yang ditelusurinama properti (string)nilai elemen
Target yang sahobject biasa, arrayarray, string, Map, Set
Pada arraymenghasilkan index sebagai teksmenghasilkan elemen
Pada object biasabekerjaditolak dengan TypeError

Sisi penolakan itu bisa dibuktikan langsung — mencoba menelusuri object dengan for-of memicu error runtime:

js
const harga = { kopi: 12000 };
try {
  for (const nilai of harga) {
    console.log(nilai);
  }
} catch (err) {
  console.log(err.name + ": " + err.message);
}

Output yang Dihasilkan:

text
TypeError: harga is not iterable

Pesan errornya tegas: object biasa bukan iterable. Untuk object, gunakan for...in atau — lebih disarankan — gabungan Object.keys(), Object.values(), dan Object.entries() dari section Objects dengan for...of, sehingga satu pola penelusuran saja yang perlu dikuasai. Kebiasaan lain yang perlu dijaga: hindari for...in pada array. Index yang dihasilkan berupa teks ("0", "1") memicu perilaku tak terduga pada operasi aritmetika sederhana.

break, continue, dan String #

Karena for...of adalah loop, break dan continue dari section Control Flow berlaku seperti biasa. String pun termasuk iterable, sehingga penelusuran per karakter ditulis tanpa index sama sekali:

js
for (const huruf of "Siti") {
  if (huruf === "t") continue;
  console.log(huruf);
}

Output yang Dihasilkan:

text
S
i
i

Huruf t dilewati continue, sehingga yang tercetak S, i, dan i — dua kemunculan huruf i menunjukkan setiap karakter diperiksa satu per satu, sesuai urutan dalam string. Dengan ini seluruh koleksi yang kita pegang — array, string, Map, Set, hingga iterable buatan sendiri — ditelusuri dengan satu sintaks yang sama.

Chapter terakhir section ini merangkum semuanya menjadi keputusan praktis: kapan array, object, Map, atau Set menjadi tempat yang tepat untuk data kita.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

antrean-pesanan.js

Unduh berkas
javascript
// antrean-pesanan.js
// for-of menelusuri nilai; entries(), keys(), values() menyertakan posisi.

const antrean = ["Es Jeruk", "Kopi Gayo", "Teh Melati"];
for (const pesanan of antrean) {
  console.log(pesanan);
}

for (const [posisi, pesanan] of antrean.entries()) {
  console.log(`${posisi + 1}. ${pesanan}`);
}

for (const posisi of antrean.keys()) {
  console.log(`slot ${posisi}`);
}

// Map dan Set juga iterable, sehingga ikut ditelusuri for-of
const stok = new Map([["Kopi Gayo", 24], ["Teh Melati", 10]]);
for (const [nama, jumlah] of stok) {
  console.log(`${nama}: sisa ${jumlah}`);
}

const belumSelesai = new Set(["P-201", "P-202"]);
for (const nomor of belumSelesai) {
  console.log(`Proses ${nomor}`);
}

// for-in menelusuri nama properti object, bukan nilai
const harga = { kopi: 12000, teh: 8000 };
for (const kunci in harga) {
  console.log(`${kunci}: Rp${harga[kunci]}`);
}