尾递归

一、什么是尾调用?

尾调用的概念非常简单,一句话就能说清楚,就是指某个函数的最后一步是调用另一个函数。

function f(x){
  return g(x);
}

二、尾调用优化

函数在调用的时候会在调用栈(call stack)中存有记录,每一条记录叫做一个调用帧(call frame),每调用一个函数,就向栈中push一条记录,函数执行结束后依次向外弹出,直到清空调用栈,参考下图:

function foo () { console.log(111); }
function bar () { foo(); }
function baz () { bar(); }

baz();
call stack

造成这种结果是因为每个函数在调用另一个函数的时候,并没有 return 该调用,所以JS引擎会认为你还没有执行完,会保留你的调用帧。

如果对上面的例子做如下修改:

function foo () { console.log(111); }
function bar () { return foo(); }
function baz () { return bar(); }

baz();

二、什么是尾递归?

前面我们知道了尾调用的概念,当一个函数尾调用自身,就叫做尾递归。

function foo () {
    return foo();
}

那么尾递归相比递归而言,有哪些不同呢?
我们通过下面这个求阶乘的例子来看一下:

function factorial (num) {
    if (num === 1) return 1;
    return num * factorial(num - 1);
}

factorial(5);            // 120
factorial(10);           // 3628800
factorial(500000);       // Uncaught RangeError: Maximum call stack size exceeded

 

如果用尾递归来计算阶乘呢?

'use strict';

function factorial (num, total) {
    if (num === 1) return total;
    return factorial(num - 1, num * total);
}

factorial(5, 1);                // 120
factorial(10, 1);               // 3628800
factorial(500000, 1);           // 分情况

ps:值得注意的是,虽然说这里启用了严格模式,但是经测试,在Chrome和Firefox下,还是会报栈溢出错误,并没有进行尾调用优化
Safari浏览器进行了尾调用优化,factorial(500000, 1)结果为Infinity,因为结果超出了JS可表示的数字范围
如果在node v6版本下执行,需要加--harmony_tailcalls参数,node --harmony_tailcalls test.js
node最新版本已经移除了--harmony_tailcalls功能

博主试了一下,下面这个函数,最多可以到 12578 次递归
function F(n) {
  if (n>12577) return n;
  return F(n+1);
}

 



posted @ 2018-12-19 14:32  张啊咩  阅读(231)  评论(0编辑  收藏  举报