"RangeError:超出最大调用堆栈大小"为什么?

Fab*_*ano 67 javascript callstack

如果我跑

Array.apply(null, new Array(1000000)).map(Math.random);
Run Code Online (Sandbox Code Playgroud)

在Chrome 33上,我明白了

RangeError: Maximum call stack size exceeded

为什么?

Der*_*會功夫 74

浏览器无法处理那么多论点.请参阅此代码段,例如:

alert.apply(window, new Array(1000000000));
Run Code Online (Sandbox Code Playgroud)

这会产生RangeError: Maximum call stack size exceeded与您的问题相同的结果.

为了解决这个问题,请:

var arr = [];
for(var i = 0; i < 1000000; i++){
    arr.push(Math.random());
}
Run Code Online (Sandbox Code Playgroud)


Far*_*hat 29

在这里它失败了Array.apply(null, new Array(1000000))而不是.map通话.

所有函数参数必须适合于callstack(至少是每个参数的指针),所以在这里它们对于callstack来说是太多的参数.

你需要了解什么是调用堆栈.

Stack是一个LIFO数据结构,就像一个只支持push和pop方法的数组.

让我通过一个简单的例子解释它是如何工作的:

function a(var1, var2) {
    var3 = 3;
    b(5, 6);
    c(var1, var2);
}
function b(var5, var6) {
    c(7, 8);
}
function c(var7, var8) {
}
Run Code Online (Sandbox Code Playgroud)

a调用此函数时,它将调用bc.当bc被调用时,局部变量a是不可访问的作用域的Javascript角色那里,因为,但JavaScript引擎必须记住的局部变量和参数,所以它会把它们推入调用堆栈.假设您正在使用像Narcissus这样的Javascript语言实现JavaScript引擎.

我们将callStack实现为数组:

var callStack = [];

每次调用一个函数时,我们都会将局部变量推送到堆栈中:

callStack.push(currentLocalVaraibles);

一旦函数调用完成(比如a,我们已经调用b,b完成执行并且我们必须返回a),我们通过弹出堆栈来获取局部变量:

currentLocalVaraibles = callStack.pop();

因此,当a我们想c再次调用时,推送堆栈中的局部变量.如你所知,编译器要有效地定义一些限制.在这里,当你做的时候Array.apply(null, new Array(1000000)),你的currentLocalVariables对象将是巨大的,因为它1000000里面会有变量.因为.apply将每个给定的数组元素作为参数传递给函数.一旦被推入调用堆栈,这将超过调用堆栈的内存限制,并将抛出该错误.

在无限递归(function a() { a() })上发生同样的错误太多次,东西已被推送到调用堆栈.

请注意,我不是编译器工程师,这只是对正在发生的事情的简化表示.它确实比这更复杂.通常推送到callstack的是栈堆栈,它包含参数,局部变量和函数地址.


Om *_*Sao 14

您首先需要了解调用堆栈。了解调用堆栈还将让您清楚地了解“函数层次结构和执行顺序”在 JavaScript 引擎中的工作原理。

调用栈主要用于函数调用(call)。因为只有一个调用栈。因此,从上到下一次推送和弹出所有函数执行。

这意味着调用堆栈是同步的。当您进入一个函数时,该函数的条目被推送到调用堆栈上,当您退出该函数时,该条目将从调用堆栈中弹出。所以,基本上如果一切都运行顺利,那么在开始和结束时,会发现调用堆栈是空的。

这是调用堆栈的说明: 在此处输入图片说明

现在,如果您提供太多参数或陷入任何未处理的递归调用。你会遇到

RangeError:超出最大调用堆栈大小

正如其他人所解释的那样,这是非常明显的。 在此处输入图片说明 在此处输入图片说明

希望这可以帮助 !