JS的函数调用栈有多深?

译者按: 有时候会遇到 Maximum call stack size exceeded 的问题,本文教你 stack size 的计算方法。

本文采用意译,版权归原作者所有

如果你写了一个一直调用自身的死循环,那么恭喜你,很快就可以看到报错:Uncaught RangeError: Maximum call stack size exceeded。那么这个 call stack size 有多少呢?

1. 计算方法

如下的方法可以为你计算出你使用的 JavaScript 引擎可以支持多深的调用(由 Ben Alman 的一段代码获得灵感):

function computeMaxCallStackSize() {
try {
return 1 + computeMaxCallStackSize();
} catch (e) {
// Call stack overflow
return 1;
}
}

运行得到如下三个结果:

  • Node.js: 11034
  • Firefox: 50994
  • Chrome: 10402

这些数字代表了什么呢?Mr.Aleph 告诉我在 V8,可调用的层数基于两个方面:1. 栈的大小;2. 每一栈帧的大小(用于记录函数参数和局部变量)。你可以在computeMaxCallStackSize声明局部变量来测试,你会发现数字变小。

2. ECMAScript 6 中尾递归优化

ECMAScript 6 支持尾递归优化:如果一个函数的最后一个操作是函数调用,那么将会用“跳转”而不是“子调用”。也就是说如果你将computeMaxCallStackSize重写成如下形式,在 ES6 的严格模式下,就会一直运行了。

function computeMaxCallStackSize(size) {
size = size || 1;
return computeMaxCallStackSize(size + 1);
}

3. 亮点评论

  • Andrei: “ECMAScript 6”版本的代码根本跑不通。虽然 size 会被更改,但是最终并没有值返回。
  • 回复 Andrei: 有趣!你不能用这段代码去计算 stack size。在 ES6 下,这段代码会一直运行,因此不会返回数据。在其它情况下,会返回 RangeError。为了使其工作,我把代码重写了一下:
var computeMaxCallStackSize = (function() {
return function() {
var size = 0;
function cs() {
try {
size++;
return cs();
} catch (e) {
return size + 1;
}
}
return cs();
};
})();

关于Fundebug

Fundebug专注于JavaScript、微信小程序、支付宝小程序线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了80亿+错误事件。欢迎大家免费试用

版权声明

转载时请注明作者 Fundebug以及本文地址:
https://blog.fundebug.com/2018/06/15/call-stack-size/

您的用户遇到BUG了吗?

体验Demo 免费使用