Rekursi: Fungsi yang Memanggil Dirinya Sendiri
Memahami rekursi: fungsi yang memanggil dirinya sendiri, kenapa base case itu wajib, dan cara menelusuri struktur data bertingkat seperti komentar berbalas.
Kenapa Rekursi Ada
Beberapa masalah punya bentuk yang "mirip dengan dirinya sendiri". Faktorial: 4! = 4 x 3!, dan 3! = 3 x 2!. Folder berisi subfolder yang berisi subfolder lagi. Komentar punya balasan, balasan punya balasan lagi. Untuk masalah seperti ini, menulis loop biasa terasa dipaksakan, sementara fungsi yang memanggil dirinya sendiri (rekursi) membaca persis seperti definisi masalahnya.
Contoh 1: Faktorial
function faktorial(n) {
if (n <= 1) return 1; // base case: titik berhenti
return n * faktorial(n - 1); // langkah rekursif
}
console.log(faktorial(5)); // 120Jalurnya seperti ini: faktorial(5) menunggu hasil faktorial(4), yang menunggu faktorial(3), terus sampai faktorial(1) yang langsung mengembalikan 1. Lalu hasilnya "naik" kembali: 2 x 1 = 2, 3 x 2 = 6, 4 x 6 = 24, 5 x 24 = 120.
Base Case: Rem Darurat
Setiap rekursi WAJIB punya base case, yaitu kondisi yang menghentikan pemanggilan diri sendiri. Tanpa base case, fungsi memanggil dirinya selamanya sampai JavaScript menyerah:
function rusak(n) {
return rusak(n - 1); // tidak pernah berhenti!
}
rusak(10); // RangeError: Maximum call stack size exceededBayangkan tumpukan piring: setiap pemanggilan rekursif menaruh piring baru di atas. Base case adalah momen kita mulai mengambil piring dari atas. Tanpa itu, tumpukannya roboh (stack overflow).
Contoh 2: Menelusuri Struktur Bertingkat
Ini kekuatan sejati rekursi. Contoh: komentar dengan balasan tak terbatas.
const komentar = [
{
teks: "Bagus!",
balasan: [
{ teks: "Setuju", balasan: [] },
{ teks: "Makasih", balasan: [
{ teks: "Sama-sama", balasan: [] }
]}
]
},
{ teks: "Halo", balasan: [] }
];
function hitungSemua(daftar) {
let total = daftar.length;
for (const k of daftar) {
total += hitungSemua(k.balasan); // rekursi untuk tiap balasan
}
return total;
}
console.log(hitungSemua(komentar)); // 5Dengan loop biasa, kamu harus tahu dulu seberapa dalam sarangnya (2 level? 10 level?). Dengan rekursi, kedalaman berapapun ditangani otomatis.
Rekursi vs Loop
Hampir semua rekursi bisa ditulis sebagai loop, dan loop biasanya lebih hemat memori. Pakai rekursi ketika strukturnya memang bertingkat atau bercabang (pohon folder, JSON bersarang, menu multi-level). Pakai loop untuk pengulangan datar yang sederhana.
Jebakan Umum
Pertama, setiap panggilan rekursif harus membuat argumennya "mendekati" base case. Kalau faktorial memanggil faktorial(n) tanpa mengurangi n, ia tidak pernah berhenti. Kedua, jangan pakai rekursi untuk puluhan ribu level kedalaman: call stack browser terbatas dan program akan crash. Ketiga, waspadai perhitungan berulang yang boros, seperti Fibonacci naif yang menghitung ulang cabang yang sama berkali-kali (solusinya: simpan hasil perhitungan atau pakai loop).
Catatan teknis: Rekursi bekerja dengan call stack, tumpukan bingkai pemanggilan fungsi. Setiap bingkai menyimpan variabel lokalnya sendiri, makanya tiap level rekursi punya nilai n yang berbeda. Inilah yang membuat pola "turun lalu naik" pada contoh faktorial bisa terjadi.
export default function App(): JSX.Element { return <h1>Hello world</h1> }
Tantangan
Hitung Total Balasan
Forum dan aplikasi chat nyata menyimpan komentar berbalas dengan kedalaman tak terbatas, dan kamu sering perlu menghitung total semuanya (misalnya untuk badge jumlah komentar). Buat fungsi totalKomentar(daftar) yang menerima array komentar berbentuk { teks, balasan: [...] } dan mengembalikan jumlah SEMUA komentar termasuk balasannya, sedalam apapun.
<!doctype html> <html> <head> <meta charset="utf-8" /> </head> <body> <h1>Halo JS</h1> <p>Buka console preview untuk melihat output.</p> <script src="index.js"></script> </body> </html>