Kenapa ini penting
Programmer profesional menghabiskan banyak waktunya untuk mencari tahu kenapa kode tidak bekerja seperti yang diharapkan. Perbedaan pemula dan profesional bukan pada jumlah bug yang mereka buat (semua orang membuat bug), melainkan pada seberapa cepat mereka menemukannya. Pemula menebar console.log("sini") di mana-mana dan menebak-nebak. Profesional membaca pesan error dengan teliti, menghentikan program di titik yang tepat, lalu memeriksa nilai variabel satu per satu.
Analoginya seperti dokter. Dokter tidak menebak penyakit dari satu gejala, ia memakai alat: stetoskop, termometer, hasil lab. Alat debugging-mu adalah DevTools browser, pernyataan debugger, beragam method console selain log, dan kemampuan membaca stack trace. Modul ini mengajarkan alat-alat itu dan, yang lebih penting, cara berpikir sistematis saat berburu bug.
Konsep inti
Membaca pesan error dan stack trace
Contoh stack trace:
TypeError: Cannot read properties of undefined (reading 'harga')
at hitungTotal (keranjang.js:12:28)
at checkout (keranjang.js:25:17)
at main (keranjang.js:31:1)
Cara membaca:
- Baris pertama: jenis error (
TypeError) dan penjelasannya. "Cannot read properties of undefined (reading 'harga')" artinya kamu menulissesuatu.hargadansesuatubernilaiundefined. - Baris
atpaling atas: lokasi persis error terjadi,hitungTotaldi filekeranjang.jsbaris 12 kolom 28. - Baris di bawahnya: siapa yang memanggil siapa (dari terdalam ke terluar).
mainmemanggilcheckout,checkoutmemanggilhitungTotal.
Sering kali bug sebenarnya bukan di baris yang error, melainkan di pemanggil yang mengirim data salah. Telusuri stack ke bawah.
Method console selain log
| Method | Kegunaan |
|---|---|
console.table(arr) |
Tampilkan array object sebagai tabel |
console.group(label) / groupEnd() |
Kelompokkan log yang berkaitan (bisa bertingkat) |
console.time(label) / timeEnd(label) |
Ukur durasi kode |
console.count(label) |
Hitung berapa kali baris tertentu dijalankan |
console.warn / console.error |
Log berwarna kuning/merah, mudah difilter |
console.assert(kondisi, pesan) |
Cetak pesan hanya jika kondisi salah |
console.dir(obj, { depth: null }) |
Tampilkan object bersarang secara lengkap |
console.trace() |
Cetak stack trace saat ini tanpa error |
Trik kecil: console.log({ total, diskon }) menampilkan nama variabel bersama nilainya.
Breakpoint di DevTools
Breakpoint menghentikan eksekusi di baris tertentu sehingga kamu bisa melihat semua variabel saat itu. Di Chrome/Edge: buka DevTools (F12 atau Ctrl+Shift+I / Cmd+Option+I), tab Sources (Firefox: Debugger), buka file, klik nomor baris.
Jenis breakpoint yang berguna:
- Line breakpoint: berhenti di baris tertentu.
- Conditional breakpoint: klik kanan nomor baris → Add conditional breakpoint, misalnya
item.harga < 0. Hanya berhenti jika kondisi benar, sangat berguna di dalam loop. - Logpoint: mencetak log tanpa mengubah kode.
- DOM breakpoint dan XHR/fetch breakpoint: berhenti saat elemen berubah atau request dikirim.
- Pause on exceptions: berhenti otomatis tepat ketika error dilempar.
Saat program berhenti, gunakan tombol kendali:
| Tombol | Fungsi |
|---|---|
| Resume (F8) | Lanjut sampai breakpoint berikutnya |
| Step over (F10) | Jalankan baris ini, tidak masuk ke function |
| Step into (F11) | Masuk ke function yang dipanggil |
| Step out (Shift+F11) | Selesaikan function saat ini, kembali ke pemanggil |
Panel Scope menampilkan semua variabel lokal dan closure, Watch memantau ekspresi pilihanmu, Call Stack menunjukkan rantai pemanggilan.
Pernyataan debugger
Menulis debugger; di kode sama dengan memasang breakpoint di baris itu, asal DevTools sedang terbuka. Jika tidak, pernyataan ini diabaikan. Di Node.js, jalankan node --inspect-brk file.js lalu buka chrome://inspect untuk men-debug dengan DevTools yang sama.
Cara berpikir sistematis
- Reproduksi: pastikan bug bisa dimunculkan dengan langkah yang jelas.
- Persempit: cari bagian terkecil yang masih menghasilkan bug (bagi dua, matikan sebagian kode).
- Hipotesis: "Saya kira
totaljadi string karena input dari form." - Buktikan: pakai breakpoint/log untuk mengecek hipotesis, bukan menebak sambil mengubah kode.
- Perbaiki dan uji ulang, termasuk kasus yang sebelumnya berhasil.
Contoh kode
// Data keranjang dengan bug tersembunyi
const keranjang = [
{ nama: "Kopi Aceh", harga: 85000, qty: 2 },
{ nama: "Teh Hijau", harga: "45000", qty: 1 }, // harga berupa string!
{ nama: "Gula Aren", harga: 30000, qty: 3 },
];
// 1. console.table: lihat data sekilas
console.table(keranjang);
// → (tabel dengan kolom (index), nama, harga, qty)
// 2. console.assert: periksa asumsi
keranjang.forEach((item) => {
console.assert(typeof item.harga === "number", `Harga ${item.nama} bukan angka:`, item.harga);
});
// → Assertion failed: Harga Teh Hijau bukan angka: 45000
// 3. console.group untuk log bertingkat
function hitungTotal(items) {
console.group("hitungTotal");
let total = 0;
for (const item of items) {
const subtotal = item.harga * item.qty; // "45000" * 1 = 45000 (perkalian mengubah ke angka)
total += subtotal;
console.log({ nama: item.nama, subtotal, total });
}
console.groupEnd();
return total;
}
console.log("Total:", hitungTotal(keranjang)); // → Total: 305000
// 4. Bug muncul saat memakai + (penjumlahan string!)
function hitungTotalBug(items) {
return items.reduce((acc, item) => acc + item.harga, 0);
}
console.log(hitungTotalBug(keranjang)); // → 850004500030000 (string, bukan 160000!)
// Perbaikan: Number(item.harga)
// 5. console.time: ukur performa
console.time("urutkan 100rb angka");
const angka = Array.from({ length: 100000 }, () => Math.random());
angka.sort((a, b) => a - b);
console.timeEnd("urutkan 100rb angka"); // → urutkan 100rb angka: 25.3ms (bervariasi)
// 6. console.count: berapa kali function dipanggil
function render() { console.count("render"); }
render(); render(); render();
// → render: 1
// → render: 2
// → render: 3
// 7. console.dir untuk object bersarang dalam
const pesanan = { id: 1, pelanggan: { alamat: { kota: { nama: "Medan", kode: "MDN" } } } };
console.dir(pesanan, { depth: null });
// → { id: 1, pelanggan: { alamat: { kota: { nama: 'Medan', kode: 'MDN' } } } }
// 8. Membaca stack trace secara program
function a() { b(); }
function b() { c(); }
function c() { null.properti; }
try {
a();
} catch (err) {
console.log(err.message); // → Cannot read properties of null (reading 'properti')
console.log(err.stack.split("\n").slice(0, 4).join("\n"));
// → TypeError: Cannot read properties of null (reading 'properti')
// → at c (/path/file.js:63:16)
// → at b (/path/file.js:62:16)
// → at a (/path/file.js:61:16)
}
// 9. debugger: berhenti di sini jika DevTools terbuka (diabaikan jika tidak)
function diskon(harga, persen) {
debugger; // periksa nilai harga & persen di panel Scope
return harga - harga * (persen / 100);
}
console.log(diskon(200000, 15)); // → 170000
Coba praktik di browser: simpan kode di atas sebagai debug.js, panggil dari file HTML (<script src="debug.js"></script>), buka DevTools tab Sources, lalu pasang conditional breakpoint di baris const subtotal = ... dengan kondisi typeof item.harga !== "number". Program hanya berhenti di item "Teh Hijau". Untuk Node.js: node --inspect-brk debug.js, buka chrome://inspect, klik inspect.
Kesalahan umum
- Hanya membaca baris pertama error. Stack trace di bawahnya menunjukkan jalur ke sumber masalah. Baca dari atas, telusuri pemanggil.
- Mengubah kode sambil menebak tanpa membuktikan hipotesis. Hentikan program dengan breakpoint dan lihat nilai sebenarnya.
console.log(obj)lalu objeknya berubah. Di browser, object yang di-log ditampilkan "hidup": saat kamu klik untuk melebarkan, isinya sudah nilai terbaru. Log salinan:console.log(structuredClone(obj))atauJSON.stringify(obj).- Lupa menghapus
debuggerdan log berlebihan sebelum kode dirilis. Gunakan linter (misalnya aturan ESLintno-debugger) untuk mendeteksinya. - Mengabaikan peringatan kuning (warning). Banyak bug besar diawali peringatan kecil.
Latihan
- Mudah, Diberikan array 5 siswa
{ nama, nilai }, tampilkan denganconsole.table, lalu hitung rata-rata dan bungkus log-nya denganconsole.group("Statistik"). - Sedang, Kode
const total = ["10", "20", "30"].reduce((a, b) => a + b, 0)menghasilkan"0102030". Jelaskan sebabnya memakaiconsole.logdi dalam callback, lalu perbaiki. - Menantang, Tulis function
ukur(label, fn)yang menjalankanfn, mengukur durasinya denganconsole.time, mencetak error keconsole.errorjikafngagal (tanpa menghentikan program), dan mengembalikan hasilnya ataunull.
Lihat solusi
// 1
const siswa = [
{ nama: "Ana", nilai: 88 }, { nama: "Bayu", nilai: 75 }, { nama: "Citra", nilai: 92 },
{ nama: "Dodi", nilai: 64 }, { nama: "Eka", nilai: 81 },
];
console.table(siswa);
console.group("Statistik");
const rata = siswa.reduce((s, x) => s + x.nilai, 0) / siswa.length;
console.log("Rata-rata:", rata); // → Rata-rata: 80
console.log("Tertinggi:", Math.max(...siswa.map((x) => x.nilai))); // → Tertinggi: 92
console.groupEnd();
// 2
["10", "20", "30"].reduce((a, b) => {
console.log({ a, b, tipeA: typeof a }); // langkah ke-2 memperlihatkan a = '010' (string)
return a + b;
}, 0);
// Penyebab: 0 + "10" = "010" (penggabungan string), dan seterusnya.
const total = ["10", "20", "30"].reduce((a, b) => a + Number(b), 0);
console.log(total); // → 60
// 3
function ukur(label, fn) {
console.time(label);
try {
return fn();
} catch (err) {
console.error(`[${label}] gagal:`, err.message);
return null;
} finally {
console.timeEnd(label);
}
}
console.log(ukur("parse ok", () => JSON.parse('{"a":1}').a)); // → parse ok: 0.05ms lalu 1
console.log(ukur("parse rusak", () => JSON.parse("{"))); // → [parse rusak] gagal: ... lalu null
Ringkasan
- Baca stack trace dari atas: jenis error, lokasi persis, lalu rantai pemanggil.
- Manfaatkan
console.table,group,time,count,assert,dir, bukan hanyalog. - Breakpoint (termasuk conditional) dan
debuggermenghentikan program untuk memeriksa nilai nyata. - Gunakan Step over/into/out serta panel Scope, Watch, dan Call Stack.
- Debug secara sistematis: reproduksi, persempit, hipotesis, buktikan, perbaiki.