尾递归以及在浏览器中的实现

这里写图片描述

这是阮一峰大神的《ES6标准入门》一书中写道的,没看过这节的童鞋可以狠戳这里

当时我就很不理解:除了最后一次调用不是尾递归,其余 y 大于 0 的时候,都是尾递归;那么,按照书“尾调用优化”上所说的,尾递归本身已经是“尾调用优化”过了,为什么还没报Uncaught RangeError: Maximum call stack size exceeded(…)——调用栈溢出这种错误?

于是,“我曾经问遍整个世界”,然而并没有“从来没得到答案”,这里我要感谢我的胡桃夹子给了我灵感��!至于谁是胡桃夹子,嘿嘿,我不告诉你,相信你以后会知道他的!

好了,废话不多说,开始分析吧。。

首先,先来了解一下什么叫栈溢出

这里写图片描述

“同一时刻只能有一个函数在运行”主要是由于JS单线程的。

另外,值得提醒的是firstFn并不是尾调用!因为,这个函数最后执行的是return undefined,只有一个函数内最后执行的语句是return xxx()才算尾调用,因此firstFn的栈对象,在secondFn执行时,并没有被释放掉。

其实,图一里面的这个函数是尾递归的,但是很坑爹的是浏览器没有实现尾递归,产生省内存的效果

你不信,你可以复制下面这段代码到你的编辑器,打开Chrome浏览器的Sources看看调用栈中每次debugger的变化。

function sum(x, y) {
  if (y > 0) {
    debugger;
    return sum(x + 1, y - 1);
  } else {
    return x;
  }
}

sum(1, 100);

你会发现了每次debugger都会产生添加新的调用栈信息,是不是很神奇?!按正常来说,我尾递归了,就应该省内存!但没有,事实上是浏览器发现尾递归之后,没有进行尾递归优化!

于是,在阮大神的书里面附上了本应该浏览器内置的“尾递归优化的实现”,其思路是递归执行转为循环执行

再次,复制下面这段代码到你的编辑器,打开Chrome浏览器的Sources看看调用栈中每次debugger的变化。

function tco(f) {
  var value;
  var active = false;
  var accumulated = [];

  return function accumulator() {
    accumulated.push(arguments);
    if (!active) {
      active = true;
      while (accumulated.length) {
        value = f.apply(this, accumulated.shift());
      }
      active = false;
      return value;
    }
  };
}

var sum = tco(function(x, y) {
  if (y > 0) {
    debugger;
    return sum(x + 1, y - 1)
  }
  else {
    return x
  }
});

sum(1, 100000)

这次就可以看到Sources的调用栈在每次debugger后,只保留了一条sum信息!

好了,解释完毕!其实,本片文章只是阮大神那本书那节的一个详细解读啦~

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值