译者按: 有时候会遇到Maximum call stack size exceeded的问题,本文教你stack size的计算方法。
- 原文: The maximum call stack size
- 译者: Fundebug
为了保证可读性,本文采用意译而非直译。另外,本文版权归原作者所有,翻译仅用于学习。
如果你写了一个一直调用自身的死循环,那么恭喜你,很快就可以看到报错: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);
}
备注:justjavac提到由于尾递归优化会导致堆栈报错信息不准确而逐渐不被支持,请大家移步评论区了解详情。
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已经服务了一年半时间,累计处理了5亿+错误事件,得到了众多知名用户的认可。Fundebug支持主流前端框架的bug监控,欢迎各位老铁体验!