1.1>简单概括:就是在函数最后一步调用另一个函数(不一定出现在函数尾部)并且不能出现赋值,加减等操作。
下面都不是尾调用:
// 情况一
function f(x){
let y=g(x);
return y;
}
// 情况二
function f(x){
return g(x)+1
}
// 情况三
function f(x){
g(x);
}
1.2>尾调用优化
“尾调用优化”就是只保留内层函数的调用帧,只要直接用内层函数的调用帧取代外层函数的调用帧就可以了。
“尾调用优化”的意义:大大节省内存,因为每次执行时,调用帧只有一项,不会发生栈溢出
注意:只有不用到外层的内部变量,内层函数的调用帧才会取代外层函数的调用帧,否则无法进行尾调用。
function f(){
let m=1;
let n=2;
return g(m+n);
}
console.log(f());
//等同于
function f(){
return g(3);
}
f();
//等同于
g(3);
function addOne(a){
var one=1;
function inner(b){
return b+one;
}
return inner(a)
}
console.log(addOne(4))
1.3>尾递归:尾调用自身就是尾递归
递归非常消耗内存,因为需要同时保存成千上百个调用帧,很容易发生“栈溢出”错误,但对于尾递归说,由于只存在一个调用帧,所以永远不会发生“栈溢出”错误
下面这个阶乘函数,计算n阶乘最多需要保存n个调用记录,复杂度是O(n)
function factorial(n){
if(n<=1){
return 1;
}else{
return n*factorial(n-1)
}
}
console.log(factorial(3));
改写成尾递归后只要保留一个调用记录,复杂度O(1)
function factorial(n,total){
if(n<=1){
return total;
}else{
return factorial(n-1,n*total)
}
}
console.log(factorial(3,1));
例子:计算Fibonacci数列
非尾递归会出现堆栈溢出
var Fibonacci=n=>n<=1?1:Fibonacci(n-1)+ Fibonacci(n-2);
console.log(Fibonacci(5));
尾递归优化过的Fibonacci数列
var Fibonacci=(n,ac1=1,ac2=1)=>n<=1?ac2:Fibonacci(n-1,ac2,ac1+ac2);
console.log(Fibonacci(100));