When it comes to function declarations, there is no inherent way to safely reference itself. If the function is renamed, using the previous name within the body would result in an error.
function fib_renamed(n){
return n < 3 ? 1 : fib(n - 1) + fib(n - 2);
// used to be correct ^^^ ^^^
}
console.log(fib(8));
Additionally, arguments.callee
throws an error in strict mode:
function fib(n){
"use strict";
return n < 3 ? 1 : arguments.callee(n - 1) + arguments.callee(n - 2);
}
console.log(fib(8));
Nevertheless, any recursive function can be customized to be name-agnostic with the Y combinator (also known as the fixed-point combinator). This allows a function to call itself without requiring a formal name. In certain languages, this might not be feasible, but in JavaScript, it can be achieved. Utilizing the Y/fixed-point combinator eliminates the need for self-references.
The function must be modified to accept one parameter representing itself and then returns a function that uses this parameter for recursive calls. Here's a simplified example using arrow functions:
//original
const fib = n => n < 3 ? 1 : fib(n - 1) + fib(n - 2);
//updated
const updated = fib =>
n => n < 3 ? 1 : fib(n - 1) + fib(n - 2);
With traditional function declarations, the process would look like this:
//original
function fib(n){
return n < 3 ? 1 : fib(n - 1) + fib(n - 2);
}
//updated
function updated(fib) {
return function (n){
return n < 3 ? 1 : fib(n - 1) + fib(n - 2);
}
}
The implementation of the Y combinator itself is:
const Y = f => (g => g (g)) (g => f (x => g (g) (x));
(from "Common combinators in JavaScript" by Avaq on GitHub)
To use the Y combinator:
const Y = f => (g => g (g)) (g => f (x => g (g) (x)));
function updated(fib) {
return function (n){
return n < 3 ? 1 : fib(n - 1) + fib(n - 2);
}
}
console.log(Y(updated)(8));
const fib_new = Y(updated);
console.log(fib_new(8));