Advanced Control Flow

Recursion and Patterns

This appendix wanders past the basics from Chapter 4 into slightly stranger territory. Powerful stuff you’ll bump into as you advance, but nothing you need on your first day. Think of it as the deleted scenes.

Recursion: When a Function Calls Itself

Here’s something that may bend your mind: a function can call itself.

function countdown(n) {
  if (n <= 0) {
    console.log("Blast off!");
    return;
  }
  console.log(n);
  countdown(n - 1); // The function calls itself!
}

countdown(5);

This prints: 5, 4, 3, 2, 1, Blast off!

Here’s how it works: the function checks if we’ve reached zero or below. If so, blast off and return. Otherwise, print the current number, then call countdown with one less. The function invokes itself, like a dream within a dream within a dream. Except this one doesn’t end with a spinning top.

Watch the call stack grow as each recursive call waits for the next one to finish. The base case is crucial.

The crucial bit is the base case: the condition that tells the recursion when to stop. Leave it out and the function calls itself forever, stacking call upon call like a queue that shuffles forward but never reaches the window, which is every bit as catastrophic as an infinite loop and for exactly the same reason. Each call quietly trusts a smaller version of itself to finish the job. It’s delegation all the way down.

The Arrow Code Antipattern: Don’t Build Caves

A particular species of problematic code, if you squint at it, looks like an arrow pointing off to the right:

if (condition1) {
  if (condition2) {
    if (condition3) {
      if (condition4) {
        if (condition5) {
          // Your actual code, buried in a cave
          doSomething();
        }
      }
    }
  }
}

This is known as “arrow code” or the “arrowhead antipattern,” and it shows up with remarkable frequency when you’re new to programming. Nobody sets out to write it. It just accretes, one innocent if at a time.

Compare arrow code with its refactored version using guard clauses.

The problems pile up. It reads poorly. It debugs worse, sending you spelunking deeper with each nested condition. You’ve built a cave, and the actual logic is cowering in the dark at the back of it.


Return to Chapter 4 for the core control flow concepts, or continue to Appendix B for advanced function patterns.