Using Named Function Expressions:
You can give a function expression a name that is actually private and is only visible from inside of the function ifself:
var factorial = function myself (n) {
if (n <= 1) {
return 1;
}
return n * myself(n-1);
}
typeof myself === 'undefined'
Here myself is visible only inside of the function itself.
You can use this private name to call the function recursively.
See 13. Function Definition of the ECMAScript 5 spec:
The Identifier in a FunctionExpression can be referenced from inside the FunctionExpression's FunctionBody to allow the function to call itself recursively. However, unlike in a FunctionDeclaration, the Identifier in a FunctionExpression cannot be referenced from and does not affect the scope enclosing the FunctionExpression.
Please note that Internet Explorer up to version 8 doesn't behave correctly as the name is actually visible in the enclosing variable environment, and it references a duplicate of the actual function (see patrick dw's comment below).
Using arguments.callee:
Alternatively you could use arguments.callee to refer to the current function:
var factorial = function (n) {
if (n <= 1) {
return 1;
}
return n * arguments.callee(n-1);
}
The 5th edition of ECMAScript forbids use of arguments.callee() in strict mode, however:
Answer from Arnaud Le Blanc on Stack Overflow(From MDN): In normal code arguments.callee refers to the enclosing function. This use case is weak: simply name the enclosing function! Moreover, arguments.callee substantially hinders optimizations like inlining functions, because it must be made possible to provide a reference to the un-inlined function if arguments.callee is accessed. arguments.callee for strict mode functions is a non-deletable property which throws when set or retrieved.
Videos
Using Named Function Expressions:
You can give a function expression a name that is actually private and is only visible from inside of the function ifself:
var factorial = function myself (n) {
if (n <= 1) {
return 1;
}
return n * myself(n-1);
}
typeof myself === 'undefined'
Here myself is visible only inside of the function itself.
You can use this private name to call the function recursively.
See 13. Function Definition of the ECMAScript 5 spec:
The Identifier in a FunctionExpression can be referenced from inside the FunctionExpression's FunctionBody to allow the function to call itself recursively. However, unlike in a FunctionDeclaration, the Identifier in a FunctionExpression cannot be referenced from and does not affect the scope enclosing the FunctionExpression.
Please note that Internet Explorer up to version 8 doesn't behave correctly as the name is actually visible in the enclosing variable environment, and it references a duplicate of the actual function (see patrick dw's comment below).
Using arguments.callee:
Alternatively you could use arguments.callee to refer to the current function:
var factorial = function (n) {
if (n <= 1) {
return 1;
}
return n * arguments.callee(n-1);
}
The 5th edition of ECMAScript forbids use of arguments.callee() in strict mode, however:
(From MDN): In normal code arguments.callee refers to the enclosing function. This use case is weak: simply name the enclosing function! Moreover, arguments.callee substantially hinders optimizations like inlining functions, because it must be made possible to provide a reference to the un-inlined function if arguments.callee is accessed. arguments.callee for strict mode functions is a non-deletable property which throws when set or retrieved.
You can access the function itself using arguments.callee [MDN]:
if (counter>0) {
arguments.callee(counter-1);
}
This will break in strict mode, however.
It's intended to exactly emulate the behavior of Array.prototype.flat, which returns a new array even if there's nothing to be flattened:
const arr = [0, 1, 2];
console.log(arr === arr.flat());
The .slice isn't necessary to produce a flattened array, it's just there to create a new array reference just in case there's no recursion to do.
The requirement of the creation of a new array is described in the specification here, and in FlattenIntoArray. Even if there aren't any elements to flatten, a new array is created by assigning all properties and values of the old array to the new array. ("Repeat, while sourceIndex < sourceLen: (assign property origArr[sourceIndex] to new array)")
Technically there is no problem if you don't modify the returned value.
However the behaviour is confusing if the returned value is modified, either by an unknowing user or by accident. A function that sometimes returns a clone and sometimes returns a reference makes it harder to debug, and is most likely considered an antipattern.
Since there is no standard for indicating whether a returned array is mutable in JavaScript, MDN most likely considered it is better to be safe and consistent
The trick to all recursion is the exit condition. Without it the function will run forever or the system runs out of memory. The exit condition for foo is if (i < 0) return;.
Now because javascript is, in this case at least, run synchronously, it will finish whatever it is doing before moving on to the next line of code.
The function calls itself with foo(i - 1); which will in turn call itself and so on until the exit condition is met. It is therefore vital that the exit condition is placed before the recursive call.
To make this easier to understand, consider the value of i for each successive call:
foo(3)
foo(2)
foo(1)
foo(0) // exit condition is met, ending the recursion
print end 0
print end 1
print end 2
print end 3
If I were to write:
console.log("begin");
my_custom_function(1);
console.log("end")
Then console.log will log begin, then my_custom_function will be called, and when my_custom_function has finished it will pick up where it left off and console.log will log end.
It's exactly the same.
When foo has finished, it will return to the calling function (which happens to also be foo) and continue where it left off.
Don't attach the counter to the function. The counter is shared by all recursive calls, so the counter represents the number of function calls, instead of the recursion depth.
When depth is passed as a separate variable, the counter shows the true depth.
function traverse(thing, depth)
{
if (typeof depth == 'number')
depth++;
else
depth = 1;
if (thing.child)
traverse(thing, depth);
}
Another (and perhaps nicer) solution would be to utilize JS functional programming strengths and use a high-order function to keep all depth-related housekeeping outside of the main function. Consider, for example, the following classic example:
function fac(n) {
if(n < 3)
return n;
return n * fac(n - 1);
}
We want this one to break the recursion once its goes deeper than a given value. Let's code a wrapper:
function wrapDepth(fn, max) {
var depth = 0
return function() {
if (++depth > max)
throw "Too much recursion"
var out = fn.apply(null, [].slice.call(arguments, 0))
depth--;
return out;
}
}
Create a wrapper with max depth = 20:
fac = wrapDepth(fac, 20)
and test:
console.log(fac(10)) // 3628800
console.log(fac(100)) // Too much recursion
Note that we didn't make any change in the main function fac itself, but still, its recursion depth is now under control.