Perulangan menjalankan blok perintah yang sama berkali-kali tanpa menulis ulang perintahnya. Topik ini membahas for klasik dengan tiga bagiannya, while dan do-while untuk jumlah putaran yang belum pasti, serta break dan continue untuk mengendalikan jalannya putaran. Materinya ditutup dengan perulangan bersarang untuk jadwal piket, dua kesalahan yang masing-masing meleset satu langkah dan tidak pernah berhenti, dan pertimbangan kapan for klasik lebih tepat daripada for...of.
for klasik tiga bagian#
Pernyataan for memadatkan tiga hal dalam satu baris, yaitu inisialisasi, kondisi, dan pembaruan. Inisialisasi dijalankan sekali di awal, kondisi dicek sebelum setiap putaran, dan pembaruan dijalankan setelah badan perulangan selesai. Bila kondisi langsung bernilai salah pada pemeriksaan pertama, badan perulangan tidak dijalankan sama sekali. Ketiga bagian dipisahkan titik koma di dalam tanda kurung, sehingga bentuk umumnya terbaca for (mulai; syarat; langkah).
Contoh klasiknya adalah menjumlahkan isi array. Variabel i berperan sebagai indeks sekaligus penghitung putaran, dan kondisi i < harga.length menjaga pembacaan tetap di dalam batas array.
const harga = [3000, 4500, 2500];
let total = 0;
for (let i = 0; i < harga.length; i += 1) {
total += harga[i];
}
console.log(`Total belanja: ${total}`);Total belanja: 10000Keluarannya 10000, hasil penjumlahan 3000, 4500, dan 2500. Perhatikan bahwa kondisinya memakai < dan bukan <=, sebuah pilihan yang mencegah satu putaran berlebih. Bila bagian pembaruan dihapus, i tidak pernah berubah dan perulangan tidak pernah berhenti, kesalahan yang diperagakan pada bagian Off-by-one dan perulangan tak berujung.
while dan do-while#
Pernyataan while hanya memuat kondisi, sedangkan persiapan dan pembaruannya ditulis manual di sekitarnya. Bentuk ini pas ketika jumlah putaran tidak diketahui di awal, misalnya menabung sampai target tertentu tanpa tahu butuh berapa minggu.
let saldo = 0;
let minggu = 0;
while (saldo < 20000) {
saldo += 5000;
minggu += 1;
}
console.log(`Target tercapai pada minggu ke-${minggu}, saldo ${saldo}`);Target tercapai pada minggu ke-4, saldo 20000Saldo 0 bertambah 5000 pada setiap putaran sampai mencapai 20000, dan penghitung minggu mencatat bahwa target tercapai pada putaran keempat. Jumlah putarannya memang bisa dihitung tangan di contoh ini, tetapi pola yang sama tetap bekerja bila setoran atau targetnya berubah saat program berjalan. Sifat khas while juga terlihat di sini, kondisinya dicek sebelum badan, sehingga saldo yang sudah melewati target sejak awal tidak memicu satu putaran pun.
Pernyataan do-while membalik urutannya, badan dijalankan dulu, barulah kondisi dicek. Bentuk ini menjamin minimal satu putaran, cocok untuk langkah yang memang harus dicoba sekali sebelum dinilai. Pada contoh berikut, hitungan mundur tiga sampai satu dicetak, lalu kata Mulai menyusul setelah perulangan selesai.
let hitungan = 3;
do {
console.log(hitungan);
hitungan -= 1;
} while (hitungan > 0);
console.log("Mulai!");3
2
1
Mulai!Perbedaan keduanya paling terlihat ketika kondisi sudah salah sejak awal. while langsung melompati badannya tanpa satu putaran pun, sedangkan do-while tetap menjalankan satu putaran sebelum berhenti.
let n = 5;
while (n < 3) {
console.log("badan while dijalankan");
}
do {
console.log(`do-while tetap berjalan satu kali, n = ${n}`);
} while (n < 3);
console.log("Selesai");do-while tetap berjalan satu kali, n = 5
Selesaibreak dan continue#
Pernyataan break menghentikan perulangan seketika, sedangkan continue melompati sisa badan pada putaran yang sedang berjalan lalu lanjut ke putaran berikutnya. Keduanya sering muncul bersama perulangan pencarian. Contoh berikut mencari bacaan suhu pertama yang melewati 30 derajat, lalu berhenti karena bacaan setelahnya tidak lagi relevan untuk pertanyaan itu.
const suhu = [24, 26, 29, 31, 27];
let indeksPanas = -1;
for (let i = 0; i < suhu.length; i += 1) {
if (suhu[i] > 30) {
indeksPanas = i;
break;
}
}
console.log(`Suhu di atas 30 pertama ada di indeks ${indeksPanas}, yaitu ${suhu[indeksPanas]}`);Suhu di atas 30 pertama ada di indeks 3, yaitu 31Tanpa break, perulangan tetap berjalan sampai indeks terakhir padahal jawabannya sudah ditemukan pada indeks 3. Variabel indeksPanas diberi nilai awal -1 sebagai penanda bahwa temuan belum ada. Penanda -1 lazim dipilih karena indeks yang sah selalu mulai dari 0, sehingga nilainya tidak mungkin tertukar dengan posisi sungguhan.
Sebaliknya, continue dipakai untuk melewati data yang tidak perlu diproses tanpa menghentikan perulangan. Pada catatan sensor di bawah, baris kosong dilompati, tetapi perulangannya tetap berjalan sampai selesai. Baris yang dilewati tetap menghabiskan satu nomor indeks, sehingga posisi baris lain tidak bergeser.
const catatan = ["suhu 24", "", "suhu 26", "", "suhu 29"];
for (let i = 0; i < catatan.length; i += 1) {
if (catatan[i] === "") {
continue; // lewati baris kosong, lanjut ke indeks berikutnya
}
console.log(`${i}: ${catatan[i]}`);
}0: suhu 24
2: suhu 26
4: suhu 29Perulangan bersarang: jadwal piket#
Perulangan bersarang adalah for di dalam for. Perulangan luar berjalan satu putaran, lalu perulangan dalam menuntaskan seluruh putarannya, setelah itu barulah giliran putaran berikutnya pada perulangan luar. Pola ini cocok untuk data berbentuk tabel, misalnya jadwal piket yang menyilangkan hari dengan sesi.
// Jadwal piket kelas: tiga hari, dua sesi per hari
const hari = ["Senin", "Selasa", "Rabu"];
const sesi = ["pagi", "siang"];
const petugas = ["Ali", "Budi", "Citra", "Dewi", "Eka", "Fajar"];
let nomor = 0;
for (let h = 0; h < hari.length; h += 1) {
for (let s = 0; s < sesi.length; s += 1) {
console.log(`${hari[h]}, sesi ${sesi[s]}: ${petugas[nomor]}`);
nomor += 1;
}
}Senin, sesi pagi: Ali
Senin, sesi siang: Budi
Selasa, sesi pagi: Citra
Selasa, sesi siang: Dewi
Rabu, sesi pagi: Eka
Rabu, sesi siang: FajarEnam baris keluaran berasal dari dua daftar yang dikalikan, tiga hari dikali dua sesi. Variabel nomor bertambah pada setiap baris sehingga daftar petugas terbaca berurutan tanpa loncat. Bila daftar sesi diperpanjang menjadi tiga entri, keluarannya menjadi sembilan baris tanpa mengubah struktur perulangannya, cukup menambah satu string pada array sesi.
Off-by-one dan perulangan tak berujung#
Off-by-one, yaitu kesalahan yang meleset satu langkah, adalah cacat paling umum pada perulangan berindeks. Bentuknya sering berupa <= yang seharusnya <, atau batas atas yang memakai length tanpa penyesuaian. Kesalahan ini tidak selalu memunculkan error, karena JavaScript hanya mengembalikan undefined untuk indeks yang melewati batas array.
const harga = [3000, 4500, 2500];
let total = 0;
// keliru: kondisi i <= harga.length membuat satu iterasi berlebih
for (let i = 0; i <= harga.length; i += 1) {
total += harga[i]; // pada i = 3, harga[3] bernilai undefined
}
console.log(`Total: ${total}`);Total: NaNKeluarannya NaN, bukan pesan error. Pada putaran keempat, harga[3] bernilai undefined, dan penjumlahan angka dengan undefined menghasilkan NaN. Cukup satu putaran berlebih untuk merusak seluruh rekap. Varian lainnya meleset pada ujung sebaliknya, misalnya mulai dari i = 1 sehingga butir pertama terlewat, atau kondisi i < harga.length - 1 yang mengorbankan butir terakhir. Kesalahan semacam ini lolos dari pembacaan sepintas karena keluarannya tampak wajar, hanya kurang satu baris.
Perulangan tak berujung (sering disebut infinite loop) terjadi ketika kondisi tidak pernah berubah menjadi salah. Penyebab paling sering adalah lupa memperbarui variabel kondisi di dalam badan while, atau pembaruan yang berjalan ke arah yang keliru, misalnya bertambah padahal kondisinya menunggu nilai mengecil. Batas darurat pada demo di bawah dibuat eksplisit supaya perilaku macetnya teramati tanpa benar-benar menggantung program.
let stok = 3;
let langkah = 0;
while (stok > 0) {
langkah += 1;
// stok tidak pernah dikurangi, sehingga kondisi selalu benar
if (langkah >= 1000) {
break; // pemberhentian darurat agar demo tetap berhenti
}
}
console.log(`Berhenti hanya karena batas darurat, setelah ${langkah} langkah`);Berhenti hanya karena batas darurat, setelah 1000 langkahPerulangan pada demo sengaja diberi batas darurat agar tetap berhenti, dan keluarannya menunjukkan bahwa satu-satunya jalan keluar adalah break itu. Dalam program sungguhan, perbaikannya ada pada logika kondisi, bukan pada batas darurat, yaitu mengurangi stok di dalam badan perulangan. Saat menguji logika perulangan yang belum pasti berhenti, pasang penghitung yang memanggil break setelah jumlah putaran wajar, seperti batas 1000 pada demo di atas, lalu lepaskan batas itu setelah logikanya terbukti benar.
Kapan for klasik lebih tepat#
JavaScript menyediakan for...of yang menelusuri isi array tanpa indeks, dan bentuk itu memang lebih ringkas untuk banyak keadaan. for klasik tetap lebih tepat dalam dua keadaan. Pertama, ketika indeks dibutuhkan sebagai data, misalnya mencetak nomor urut, membandingkan butir dengan tetangganya, atau mencatat posisi temuan seperti pada pencarian suhu di atas. Kedua, ketika putaran perlu melompat, misalnya maju dua langkah sekaligus dengan i += 2, atau berjalan mundur dari akhir array dengan i -= 1.
Bentuk while menempati posisi tengah. Bila jumlah putaran baru diketahui saat program berjalan, seperti saldo yang menunggu target, while menyatakan niat itu lebih langsung daripada for. Batas pada for tampak seperti angka pasti, padahal jumlah putaran aslinya belum tentu diketahui. Kesimpulannya, pilih bentuk yang menyatakan maksudnya paling jelas, dan sisakan for klasik untuk kebutuhan indeks serta lompatan.
Kesalahan umum#
- Memakai
<=bersamalength. Kondisii <= daftar.lengthmenjalankan satu putaran berlebih dan membacaundefinedpada indeks terakhir. Gunakan<, dan uji hasilnya dengan data yang jumlah butirnya mudah dihitung. - Mengubah batas di dalam badan perulangan. Menambah elemen pada array yang sedang ditelusuri membuat
lengthberubah di tengah jalan, sehingga jumlah putaran ikut memanjang atau tidak pernah selesai. Kumpulkan dulu data yang akan ditambahkan ke array terpisah, lalu masukkan ke array aslinya setelah perulangan selesai. - Menulis penghitung tanpa
let. Bentukfor (i = 0; ...)secara tidak sengaja menciptakan variabelidi lingkup luar, dan sisa nilainya bisa menyebar ke kode lain. Selalu deklarasikan penghitung denganletpada bagian inisialisasi.
Ringkasan#
formemadatkan inisialisasi, kondisi, dan pembaruan dalam satu baris, dan kondisinya dicek sebelum setiap putaran dimulai.whilecocok untuk jumlah putaran yang belum pasti, sedangkando-whilemenjamin satu putaran sebelum kondisinya dicek.breakmenghentikan perulangan,continuemelompati satu putaran, dan perulangan bersarang menghasilkan jumlah putaran sebesar hasil kali panjang kedua daftar.- Off-by-one berupa kondisi yang meleset satu langkah dan tidak selalu memunculkan error, sedangkan perulangan tak berujung terjadi bila kondisi tidak pernah berubah.
Referensi#
- for — MDN — dokumentasi resmi tiga bagian pernyataan for beserta contohnya.
- while — MDN — dokumentasi while dan do-while beserta syarat penghentiannya.
- Loops and iteration — MDN — panduan perulangan yang mencakup break, continue, dan kesalahan umumnya.