Recursion is a function solving a problem by calling itself with a smaller version of that same problem, until it reaches a version simple enough to answer directly.
function factorial(n) {
if (n <= 1) {
return 1 // base case — stops the recursion
}
return n * factorial(n - 1) // recursive case
}
factorial(5) // 5 * 4 * 3 * 2 * 1 = 120Without a condition that stops it, a recursive function calls itself forever — or more precisely, until the call stack runs out of room.
function countDown(n) {
// Missing base case — this never stops on its own
console.log(n)
countDown(n - 1)
}
// countDown(5) eventually throws:
// "Maximum call stack size exceeded"The most common recursion bug
Every recursive function needs a base case that's actually reachable — a base case that never triggers (a wrong comparison, an argument that never converges toward it) fails exactly the same way as no base case at all.
Each recursive call adds a new frame to the call stack (from the earlier Event Loop lesson) — the function doesn't actually finish until every deeper call it made has returned.
factorial(3)
// factorial(3) calls factorial(2)
// factorial(2) calls factorial(1)
// factorial(1) returns 1
// factorial(2) returns 2 * 1 = 2
// factorial(3) returns 3 * 2 = 6| Recursion | A loop |
|---|---|
| Often reads more naturally for a problem that's naturally recursive (traversing a tree, nested data) | Usually faster and uses less memory for a simple repeated task |
| Each call adds a call stack frame — deep recursion can hit a stack size limit | No stack depth concern regardless of how many iterations |
Practice it interactively
This site's Recursion tool (under Tools) visualizes the call tree unwinding step by step for a piece of code — a clearer way to see what "each call waits for the next" actually looks like than tracing it by hand.