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

如果我跑

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

在Chrome 33上,我明白了

RangeError: Maximum call stack size exceeded

为什么?

浏览器不能处理那么多的参数。 看这个例子,例如:

 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调用失败。

所有的函数参数都必须适用于callstack(至less是每个参数的指针),所以在这个参数中,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) { } 

当这里调用函数a ,它会调用bc 。 当调用bc ,由于Javascript的作用域angular色, a的局部variables不可访问,但是JavaScript引擎必须记住局部variables和参数,所以它会将它们推入调用堆栈。 假设您正在使用像Narcissus这样的Javascript语言来实现JavaScript引擎。

我们实现callStack作为数组:

var callStack = [];

每次调用一个函数,我们将局部variables推入堆栈:

callStack.push(currentLocalVaraibles);

一旦函数调用完成(如在a ,我们已经调用bb完成执行,我们必须返回a ),我们通过popup堆栈来取回局部variables:

currentLocalVaraibles = callStack.pop();

所以当我们想再次调用c时,推入堆栈中的局部variables。 如您所知,编译器要高效地定义一些限制。 这里当你在做Array.apply(null, new Array(1000000)) ,你的currentLocalVariables对象将是巨大的,因为它里面会有1000000variables。 由于.apply会将每个给定的数组元素作为parameter passing给函数。 一旦推入调用堆栈,将超出调用堆栈的内存限制,并会抛出该错误。

同样的错误发生在无限recursion( function a() { a() } )太多的时候,东西已经被推到调用堆栈。

请注意,我不是编译器工程师,这只是对正在发生的事情的简化表示。 这确实比这更复杂。 一般来说,推送到调用堆栈的内容被称为堆栈框架 ,其中包含参数,局部variables和函数地址。

for的答案是正确的,但是如果你真的想使用函数式避免语句 – 你可以使用下面的代替expression式:

Array.from(Array(1000000),()=> Math.random());

Array.from()方法从类似数组或类的对象中创build一个新的数组实例。 这个方法的第二个参数是一个map函数来调用数组的每个元素。

遵循相同的想法,您可以使用ES2015 Spread操作符重写它:

[… Array(1000000)]。map(()=> Math.random())

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

[… Array(1000000)]。map((_,i)=> i + Math.random())