递归是非常耗内存的,因为需要同时保存成千个调用帧,容易发生栈溢出错误,但如果使用尾递归的话,由于只存在一个调用帧,所以不会发生栈溢出错误.
- 例1: 阶乘计算
function factorial (n) {
if (n === 1) return 1;
return n * factorial(n - 1) ;
}
factorial(5) // 120
factorial(100) // Stack Overflow error
上述代码是一个阶乘函数,尾部有赋值,不属于函数尾调用,计算n次阶乘会有n个调用帧,解决方法如下:
function factorial (n , total) {
if (n === 1) return total ;
return factorial(n - 1, n *total );
}
factorial(5, 1) // 120
但是这样写有个不直观的缺点,优化如下
优化1:
function tailFactorial (n , total) {
if (n === 1) return total ;
return tailFactorial(n - 1 , n *total);
}
function factorial(n) {
return tailFactorial(n , 1);
}
factorial(5) // 120
优化2:柯里化
function currying(fn, n) {
return function (m) {
return fn.call(this , m, n) ;
}
}
function tailFactorial (n , total) {
if (n === 1) return total ;
return tailFactorial(n - 1, n * total);
}
const factorial = currying(tailFactorial, 1) ;
factorial(5) // 120
优化3: es6函数默认值
function factorial (n, total = 1) {
if (n === 1) return total;
return factorial(n - 1 , n *total) ;
}
factorial(5) // 120
- 例2: 计算斐波那契数列第n个数值
function Fibonacci(n) {
if ( n <= 1 ) {return 1};
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
Fibonacci(10) //89
Fibonacci(100) //栈溢出错误
解决:
function Fibonacci2 (n , acl = 1, ac2 = 1) {
if ( n <= 1) {return ac2} ;
return Fibonacci2 (n - 1, ac2, acl + ac2);
}