In some cases, you might want to call a function from inside that exact same function. This concept is known as Recursion.
A recursive function is simply a function that calls itself. While it can be a beautiful and elegant solution to rather complex programming problems, there are some important rules to keep in mind.
When a function calls itself, it creates a loop. But what happens if we never tell that loop to stop?
What do you think this code will do?
function getRecursive(nr) {
console.log(nr);
getRecursive(--nr); // The function calls itself!
}
getRecursive(3);
Warning: If you run this, it prints 3, 2, 1, 0, -1, -2... and it never stops counting down! Eventually, the browser will crash and throw a "Maximum call stack size exceeded" error.
Why is it not stopping? Because we are not telling the function when it should stop calling itself.
To fix the infinite loop, every recursive function must have a stop condition (often called a Base Case by programmers).
Look at our improved version:
function getRecursive(nr) {
console.log(nr);
// The Stop Condition (Base Case)
if (nr > 0) {
getRecursive(--nr);
}
}
getRecursive(3);
This function is going to call itself until the value of the parameter is no longer bigger than 0. Once nr hits 0, the if condition becomes false, the function stops calling itself, and the recursion safely ends.
What actually happens behind the scenes when we call a function recursively is that JavaScript goes one function "deeper" every time.
Because the newest function call must finish before the previous one can continue, the very first function call is actually the last one to finish. For our getRecursive(3) function, the order of execution looks like this:
getRecursive(3) startsgetRecursive(2) startsgetRecursive(1) startsgetRecursive(0) startsgetRecursive(0) executiongetRecursive(1) executiongetRecursive(2) executiongetRecursive(3) executionThe next recursive function will perfectly demonstrate how the functions stack up and then "unwind" in reverse order:
function logRecursive(nr) {
console.log("Started function:", nr);
if (nr > 0) {
logRecursive(nr - 1);
} else {
console.log("Done with recursion");
}
console.log("Ended function:", nr);
}
logRecursive(3);
Notice how the "Ended" logs happen in completely reverse order? That is because logRecursive(3) cannot log its "Ended" message until logRecursive(2) has completely finished its job!
Recursive functions can be incredibly powerful in specific contexts.
However, it must be kept in mind that, in general, the performance of recursion is slightly worse than the performance of regular iteration using a loop.
Every time a recursive function calls itself, it takes up a little bit of memory on the "call stack". If your recursion goes thousands of layers deep, it could cause a bottleneck situation that slows down your application or crashes the browser. In those cases, you might want to consider using a standard while or for loop instead.
What is absolutely required in every recursive function to prevent the browser from crashing due to an infinite loop?