๐Ÿ” What is recursion?

Recursion is when a function calls itself to solve smaller instances of a problem, until it reaches a base case that ends the loop.

It's like looking into a mirror facing another mirror โ€” the pattern repeats until it fades.


๐Ÿ“Œ Anatomy of a recursive function

Every recursive function has two parts:

  1. Base Case: The condition that stops recursion.
  2. Recursive Case: The part where the function calls itself.
function countdown(n) {
  if (n <= 0) return;     // base case
  console.log(n);
  countdown(n - 1);       // recursive case
}
countdown(3); // 3, 2, 1

๐Ÿง  Why use recursion?

Recursion is ideal when a problem:

  • Has a naturally nested or hierarchical structure (like trees, folders, JSON).
  • Can be broken into smaller subproblems (like factorial, Fibonacci).
  • Requires backtracking or exploring all paths (like permutations, maze solvers).

๐Ÿ” Iteration vs recursion

Iteration:

for (let i = 5; i > 0; i--) console.log(i);

Recursion:

function print(n) {
  if (n === 0) return;
  console.log(n);
  print(n - 1);
}

Both work โ€” but recursion can make code cleaner for problems with nested depth (like traversing a DOM tree).


๐Ÿงฎ Classic examples

1. Factorial

function factorial(n) {
  if (n === 0) return 1;
  return n * factorial(n - 1);
}
factorial(5); // 120

2. Fibonacci

function fibonacci(n) {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
}
fibonacci(6); // 8
> โš ๏ธ Be careful: this version is exponential. You can optimize it with memoization.

๐ŸŒฒ Real-world use: tree traversal

Imagine this nested folder structure:

const tree = {
  name: 'root',
  children: [
    { name: 'child1', children: [] },
    {
      name: 'child2',
      children: [
        { name: 'subchild', children: [] }
      ]
    }
  ]
};

Recursive traversal:

function printTree(node) {
  console.log(node.name);
  for (const child of node.children) {
    printTree(child);
  }
}

๐Ÿ’ก Tips for writing recursive code

  • Always define a clear base case to avoid infinite loops.
  • Think in terms of smaller subproblems.
  • Consider tail recursion or memoization for performance.
  • Use the call stack wisely: too much recursion = stack overflow.

๐Ÿงช Bonus: flattening nested arrays

function flatten(arr) {
  return arr.reduce((acc, el) =>
    Array.isArray(el) ? acc.concat(flatten(el)) : acc.concat(el)
  , []);
}

flatten([1, [2, [3, 4]], 5]); // [1, 2, 3, 4, 5]

๐Ÿงต Conclusion

Recursion can feel magical โ€” or intimidating. But once you grasp its structure and use cases, it becomes a powerful mental model, especially for:

  • Trees (DOM, JSON)
  • Graphs
  • Backtracking (games, puzzles)
  • Functional programming