“ RangeError: 超过最大调用堆栈大小”为什么?

如果我逃跑

Array.apply(null, new Array(1000000)).map(Math.random);

在 Chrome 33上,我得到了

RangeError: Maximum call stack size exceeded

为什么?

292659 次浏览

浏览器无法处理这么多参数:

alert.apply(window, new Array(1000000000));

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

要解决这个问题,请做:

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

在这里,它在 Array.apply(null, new Array(1000000))而不是 .map调用失败。

所有函数参数都必须适合调用堆栈(至少每个参数的指针) ,因此对于调用堆栈来说,它们的参数太多了。

你需要了解什么是 呼叫栈

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) {
}

当这里调用函数 a时,它将调用 bc。当调用 bc时,由于 Javascript 的作用域角色,a的本地变量无法访问,但是 Javascript 引擎必须记住本地变量和参数,因此它会将它们推入调用堆栈。假设您正在使用类似 水仙花的 JavaScript 语言实现 JavaScript 引擎。

我们将 callStack 实现为数组:

var callStack = [];

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

callStack.push(currentLocalVaraibles);

一旦函数调用完成(像在 a中一样,我们调用了 bb完成了执行,我们必须返回到 a) ,我们通过弹出堆栈返回本地变量:

currentLocalVaraibles = callStack.pop();

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

同样的错误发生在无限递归(function a() { a() })上,因为有太多次,内容被推送到调用堆栈。

请注意,我不是一个编译器工程师,这只是一个简化的表示什么是正在发生的。实际上要比这个复杂得多。通常,推送到调用堆栈的内容称为 堆叠式框架,它包含参数、局部变量和函数地址。

for的答案是正确的,但是如果你真的想使用函数风格来避免使用 for语句,你可以使用以下代替你的表达式:

数组(Array (1000000) ,() = > Math.Random ()) ;

From ()方法从类似于数组或可迭代的对象创建一个新的 Array 实例。该方法的第二个参数是一个用于调用数组中每个元素的 map 函数。

遵循同样的思路,你可以使用 ES2015传播操作员重写它:

[ ... Array (1000000)] . map (() = > Math.Random ())

在这两个例子中,如果您需要,您可以获得迭代的索引,例如:

[ ... Array (1000000)] . map ((_,i) = > i + Math.Random ())

您首先需要了解 Call Stack。理解调用堆栈还可以让您清楚地了解 JavaScript 引擎中“函数层次结构和执行顺序”是如何工作的。

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

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

下面是调用堆栈的例子: enter image description here

现在,如果你提供了太多的参数或者在任何未处理的递归调用中被捕获

超过最大调用堆栈大小

这是很明显的,正如其他人所解释的那样。 enter image description here enter image description here

希望这个能帮上忙!

根据我的经验,这个错误是由一个永远不会终止的递归函数造成的。所以我设置了一个递归必须停止执行并返回(中断)的条件。 因此,通过添加下面的代码行,我能够摆脱这个错误。

if (debtTypesCounter === debtTypesLength) return;

所以,你可以调整这个想法,以适应你的条件。希望这有帮助吗?