“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
,它会调用b
和c
。 当调用b
和c
,由于Javascript的作用域angular色, a
的局部variables不可访问,但是JavaScript引擎必须记住局部variables和参数,所以它会将它们推入调用堆栈。 假设您正在使用像Narcissus这样的Javascript语言来实现JavaScript引擎。
我们实现callStack作为数组:
var callStack = [];
每次调用一个函数,我们将局部variables推入堆栈:
callStack.push(currentLocalVaraibles);
一旦函数调用完成(如在a
,我们已经调用b
, b
完成执行,我们必须返回a
),我们通过popup堆栈来取回局部variables:
currentLocalVaraibles = callStack.pop();
所以当我们想再次调用c
时,推入堆栈中的局部variables。 如您所知,编译器要高效地定义一些限制。 这里当你在做Array.apply(null, new Array(1000000))
,你的currentLocalVariables
对象将是巨大的,因为它里面会有1000000
variables。 由于.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())