"A function that calls itself sounds like a bug. Turns out it's one of the most powerful ideas in programming."
When you first hear "a function that calls itself" — your brain says wait, wouldn't that just loop forever? Good instinct. That's exactly the right question to ask. And the answer is what makes recursion beautiful.
What is Recursion?
Recursion is when a function calls itself to solve a smaller version of the same problem — until it hits a point where it doesn't need to anymore.
Every recursive function has two parts:
- Base case — the condition that stops the recursion. Without this, you get infinite calls and a crashed browser.
- Recursive case — where the function calls itself with a smaller input, working towards the base case.
💡 Think of it like: Russian nesting dolls. You keep opening a doll to find a smaller one inside — until you reach the tiniest one that doesn't open. That's your base case.
The Call Stack — A Quick Mention
When a function calls itself, JavaScript doesn't forget what it was doing. Each call gets stacked on top of the previous one — waiting for the one above it to finish first.
countdown(3)
countdown(2)
countdown(1)
countdown(0) ← base case hit, start returning
↩ back to countdown(1)
↩ back to countdown(2)
↩ back to countdown(3)
This stack of waiting calls is called the call stack. Once the base case is reached, the stack unwinds — each call finishes and returns, one by one.
Example 1 — Countdown
Let's start simple. Count down from any number to zero.
Without recursion:
function countdown(n) {
for (let i = n; i >= 0; i--) {
console.log(i);
}
}
countdown(3); // 3, 2, 1, 0
With recursion:
function countdown(n) {
if (n < 0) return; // base case — stop here
console.log(n);
countdown(n - 1); // recursive case — call with a smaller number
}
countdown(3); // 3, 2, 1, 0
Here's what happens step by step:
countdown(3) → prints 3, calls countdown(2)
countdown(2) → prints 2, calls countdown(1)
countdown(1) → prints 1, calls countdown(0)
countdown(0) → prints 0, calls countdown(-1)
countdown(-1) → n < 0, returns. Done.
💡 The base case here is
n < 0— once we go below zero, stop. Without it, this runs forever.
Example 2 — Factorial
The factorial of a number is that number multiplied by every positive integer below it.
5! = 5 × 4 × 3 × 2 × 1 = 120
4! = 4 × 3 × 2 × 1 = 24
1! = 1
0! = 1 (by definition)
Factorial is a perfect recursion problem because each step is just: "this number times the factorial of the number below it."
Without recursion:
function factorial(n) {
let result = 1;
for (let i = n; i > 1; i--) {
result *= i;
}
return result;
}
factorial(5); // 120
With recursion:
function factorial(n) {
if (n <= 1) return 1; // base case
return n * factorial(n - 1); // recursive case
}
factorial(5); // 120
Here's what that looks like unwinding:
factorial(5)
= 5 * factorial(4)
= 5 * 4 * factorial(3)
= 5 * 4 * 3 * factorial(2)
= 5 * 4 * 3 * 2 * factorial(1)
= 5 * 4 * 3 * 2 * 1
= 120
💡 The base case is
n <= 1— because1! = 1and0! = 1. Once we hit it, the stack starts returning values back up the chain.
Recursion vs Iteration — Which Should You Use?
Both can solve the same problems. Here's how to think about it:
| Recursion | Iteration (loops) | |
|---|---|---|
| Readability | Clean for complex problems | Clean for simple problems |
| Performance | Can be slower (call stack overhead) | Usually faster |
| Best for | Trees, nested structures, divide & conquer | Counting, simple repetition |
💡 If you can solve it easily with a loop — use a loop. Reach for recursion when the problem naturally breaks into smaller versions of itself.
The Two Rules of Recursion
Before you write any recursive function, ask yourself:
- What is my base case? — When do I stop?
- Am I moving towards it? — Is each recursive call working towards the base case?
If you can't answer both questions, you're not ready to write the function yet.
Recursion isn't magic — it's just a function with a very good memory and a clear exit plan.
And now you know when to use it.
Happy coding!!! 😊
Top comments (0)