以下是普通的递归形式的计算阶乘的函数:
function factorial1(num)
{
return num<1 ? 1 : num*factorial1(num-1);
}
console.log(factorial1(5)); //结果为120
但是进行长递归时,每次调用递归函数都会消耗资源存储递归产生的变量且在递归过程中无法释放,于是将该函数进行尾递归优化:
function factorial2(num,res=1)
{
return num<1 ? res : factorial2(num-1,res*num);
}
console.log(factorial2(5)); //结果为120
相比factorial1,factorial2的参数res保存了临时的计算结果, 函数返回回溯时不需要做任何额外的计算,但是仍然会进行递归调用导致调用栈增长,于是将递归改为循环:
//递归转化为循环的形式
function trampoline(f)
{
let func = f;
while (typeof func === 'function'){
func = func();
}
return func;
}
function fac(num,res)
{
return num<1? res : fac.bind(null, num-1, res*num);
}
function factorial3(num)
{
return trampoline(fac.bind(null, num, 1));
}
console.log(factorial3(5)); //结果为120
bind方法会创建一个绑定函数,第一个参数的值为函数域内this的指向,
第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数,
这里fac函数并不会直接进行递归调用而是返回下一步的调用交给trampoline函数来循环调用, 这样调用栈就不会增长了,此方法可以解决一些浏览器进行长递归由于没有立即释放变量导致的堆栈溢出问题