本文通过具体代码测试了Node.js、Firefox和Chrome环境下JavaScript的最大递归调用次数,结果显示各引擎限制不同。文章进一步解释了V8引擎中堆栈大小与帧大小对递归深度的影响,并介绍了ES6严格模式下的尾调用优化机制,该机制可使特定递归函数无限执行而不溢出。
JavaScript最大递归深度测试
你是否好奇JavaScript引擎允许的最大递归调用次数是多少?
最大递归调用次数
可以通过以下函数来探测这一极限(灵感源自Ben Alman的gist):
代码如下:
function computeMaxCallStackSize() {
try {
return 1 + computeMaxCallStackSize();
} catch (e) {
// 调用栈溢出
return 1;
}
}
测试结果如下:
代码如下:
Node.js: 11034
Firefox: 50994
Chrome: 10402
V8引擎递归限制因素
这些数值代表什么?正如Aleph先生所指出的,在V8引擎中,递归调用的数量取决于两个因素:堆栈的总大小以及每个堆栈帧(用于保存参数和局部变量)的大小。你可以在 computeMaxCallStackSize() 函数中添加局部变量进行验证,这会导致返回的数值降低。
ES6尾调用优化机制
ES6中的尾调用优化(Tail Call Optimization) ES6标准引入了尾调用优化:如果函数执行的最后一步是调用另一个函数,引擎会将其视为“跳转”而非子函数调用。这意味着在ES6(严格模式下),只需对 computeMaxCallStackSize 函数稍作修改,它就可以无限执行下去。
代码如下:
function computeMaxCallStackSize(size) {
size = size || 1;
return computeMaxCallStackSize(size + 1);
}