DAY 9 / 30 · WEEK 2

Recursion & the Call Stack

Goal of the day

Concept

Recursion is like asking a question to someone who doesn't know the answer, so they ask someone else the same question but for a smaller version of the problem — and so on, until someone (the base case) knows the answer directly without asking anyone else. Then everyone reports their answer back up the chain, each person using the answer they received to compute their own.

Under the hood, JavaScript tracks this chain with an actual data structure: the call stack. Every function call pushes a new frame onto the stack — remembering where to resume once that call returns. Every return pops the top frame off and hands its value back to whoever called it. A recursive function without a correct base case never stops pushing — and since the stack has a fixed size limit, that crashes with a RangeError: Maximum call stack size exceeded.

Diagram

Calling factorial(3) — the stack grows (newest call on top) factorial(1) — base case factorial(2) factorial(3) factorial(1) returns 1 directly. Returning — the stack shrinks factorial(1) already popped off the top factorial(2) = 2 × 1 factorial(3) factorial(1) already popped. factorial(2) resumes, multiplies by the 1 it got back → returns 2.

Interactive visualizations

currently executing returning this step waiting on the stack

1. factorial(5) — linear recursion

Press Play or Step to begin.

2. Reverse a string recursively

Press Play or Step to begin.

The code

// Every recursive function needs exactly these two parts:

function factorial(n) {
  if (n <= 1) return 1;              // BASE CASE — stops the recursion
  return n * factorial(n - 1);       // RECURSIVE CASE — smaller sub-problem
}

function reverseStr(s) {
  if (s === '') return '';           // BASE CASE — empty string reverses to itself
  return reverseStr(s.slice(1)) + s[0]; // RECURSIVE CASE — reverse the rest, append first char
}

Line by line:

Dry run: factorial(4)

CallActionWaiting on
factorial(4)4 > 1, call factorial(3)factorial(3)'s result
factorial(3)3 > 1, call factorial(2)factorial(2)'s result
factorial(2)2 > 1, call factorial(1)factorial(1)'s result
factorial(1)1 <= 1 — base case, return 1
factorial(2) resumes2 × 1 = 2, return 2
factorial(3) resumes3 × 2 = 6, return 6
factorial(4) resumes4 × 6 = 24, return 24

Complexity

ApproachTimeSpaceWhy
Recursive factorial(n)O(n)O(n)n calls deep — every waiting frame sits on the call stack until it returns.
Iterative factorial(n)O(n)O(1)A single loop variable, no call stack growth at all.

Same time complexity, very different space — this is the classic recursion trade-off: recursive code often reads more clearly, but it costs O(depth) stack space that an equivalent loop wouldn't need.

Interview corner

How to talk through it out loud: state the base case and recursive case explicitly before writing code — "the base case is an empty string, which reverses to itself; the recursive case reverses everything after the first character, then appends it." Interviewers are listening for that structure, not just working code.

Practice problems

1. Fibonacci Number (Easy — LeetCode: Fibonacci Number)

Given n, return the nth Fibonacci number (F(0)=0, F(1)=1, F(n)=F(n-1)+F(n-2)).

2. Power of Two (Easy — LeetCode: Power of Two)

Given an integer, return true if it's a power of two.

3. Pow(x, n) (Medium — LeetCode: Pow(x, n))

Implement x^n (x to the power n), where n can be negative.

Quiz

1. Every correct recursive function needs:

2. Each recursive call does what to the call stack?

3. What happens when recursion goes too deep in JavaScript?

4. What's the space complexity of the recursive factorial(n), and why?

5. Which of these is just as broken as having no base case at all?