Recursion in JavaScript: a function that calls itself

A recursive function calls itself on a smaller problem until it reaches a base case. Ideal for anything nested to an unknown depth.
3 min read
Believemy logo

Counting the files of a folder that contains folders, walking a multi-level menu, flattening a nested structure: no simple loop is enough, because you do not know in advance how many levels exist.

Recursion answers exactly that shape of problem: solving a case by reducing it to a smaller version of itself.


Definition

A function is recursive when it calls itself. It rests on two inseparable pieces: a base case, which returns a value without calling again, and a recursive case, which calls itself on a reduced version of the problem.

JAVASCRIPT
function factorial(n) {
  if (n <= 1) return 1;          // base case
  return n * factorial(n - 1);   // recursive case
}

console.log(factorial(5)); // 120

Without the base case, the function never stops and the Call stack overflows within milliseconds. It is the first thing to write, even before the recursive call.


Where it is genuinely better

Factorial is there to explain, not to convince: a loop would do just as well. Recursion wins when the structure itself is nested.

JAVASCRIPT
const menu = {
  name: "Root",
  children: [
    { name: "Courses", children: [{ name: "JavaScript", children: [] }] },
    { name: "Blog", children: [] },
  ],
};

function count(node) {
  return 1 + node.children.reduce((n, child) => n + count(child), 0);
}

console.log(count(menu)); // 4

Writing the same thing with a loop would mean maintaining your own list of nodes left to visit. The recursive version fits on one line because it lets the stack do that work.


Recursion or loop?

  • Nested structure of unknown depth: recursion, no hesitation.
  • Linear walk over a list: a loop or a Array.reduce(), more direct and with no depth limit.
  • Very large volumes: the loop, because every recursive call consumes a stack entry.
Good to know

A recursive function that recomputes the same values over and over, like the Fibonacci sequence, becomes usable as soon as you add a cache. That is the role of Memoization.


Frequently asked questions

Question

At what depth does it break?

The order of magnitude sits around ten thousand nested calls, with no guarantee: the limit depends on the engine and the available memory. A file tree or a menu will never get close. A list of a hundred thousand items walked recursively will, and a loop becomes necessary.


Question

Does JavaScript optimize tail recursion?

The specification provides for it, but no mainstream engine implements it outside Safari. Writing a function in tail position, where the recursive call is the very last operation, therefore offers no protection against overflow. Do not count on it.


Question

How do I debug a recursion that spirals?

Print the arguments on every entry into the function: if the value does not shrink from one call to the next, the base case will never be reached. That is almost always the explanation, and the fix sits either in the stopping condition or in the value passed to the next call.

Related terms

Discover our javaScript glossary

Every word of JavaScript explained simply: keywords, built-in objects, methods, errors and concepts. Clear definitions and examples that actually run, to learn and to troubleshoot.

Share this article

Want to help us? Share this article on your networks or even better: on your site, in an article or in your newsletter.