如果我跑步
Array.apply(null, new Array(1000000)).map(Math.random);
在Chrome 33上,我得到
RangeError: Maximum call stack size exceeded
为什么?
如果我跑步
Array.apply(null, new Array(1000000)).map(Math.random);
在Chrome 33上,我得到
RangeError: Maximum call stack size exceeded
为什么?
for
循环而真的想使用它,map
则可以使用这种慢得多的方法(至少我希望是这样)Object.keys([].concat(Array(10000001).join().split(''))).map(Math.random)
console.time('object'); var arr = Object.keys([].concat(Array(1000001).join().split(''))).map(Math.random) console.timeEnd('object'); console.time('loop'); var arr = []; var i = 1000000, while(i--){ arr.push(Math.random()); } console.timeEnd('loop');
对象快2倍。
Answers:
在这里,它失败Array.apply(null, new Array(1000000))
而不是.map
呼叫。
所有函数参数必须适合调用堆栈(至少每个参数的指针),因此在此,它们对于调用堆栈而言太多了。
您需要了解什么是调用堆栈。
堆栈是一种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
被调用时,局部变量a
是不可访问的作用域的Javascript角色那里,因为,但JavaScript引擎必须记住的局部变量和参数,所以它会把它们推入调用堆栈。假设您正在使用类似Narcissus的Java语言实现JavaScript引擎。
我们将callStack实现为数组:
var callStack = [];
每当一个函数调用我们将局部变量推入堆栈时:
callStack.push(currentLocalVaraibles);
一旦函数调用完成(如in中a
,我们已经调用b
,b
完成执行,必须返回a
),我们将通过弹出堆栈来获取局部变量:
currentLocalVaraibles = callStack.pop();
因此,当a
我们要c
再次调用时,将局部变量压入堆栈。众所周知,高效的编译器会定义一些限制。在这里,当您执行操作时Array.apply(null, new Array(1000000))
,您的currentLocalVariables
对象将非常庞大,因为其中将包含1000000
变量。因为.apply
将每个给定的数组元素作为函数的参数传递。一旦推送到调用堆栈,它将超出调用堆栈的内存限制,并且将引发该错误。
在无限递归(function a() { a() }
)上发生相同的错误,次数过多,已将内容推入调用堆栈。
请注意,我不是编译器工程师,这只是正在发生的事情的简化表示。确实比这更复杂。通常,推送到调用堆栈的内容称为堆栈框架,其中包含参数,局部变量和函数地址。
Array.prototype.push.apply
被使用并传递了一个非常大的参数列表。在我看来,该函数的参数列表过大。我确信这是递归问题。再次感谢你!
您首先需要了解Call Stack。了解调用堆栈还将使您更清楚地了解“函数层次结构和执行顺序”在JavaScript Engine中的工作方式。
调用堆栈主要用于函数调用(调用)。由于只有一个调用堆栈。因此,所有功能的执行一次从上到下一次被弹出。
这意味着调用堆栈是同步的。当您输入一个函数时,该函数的条目将被推送到调用堆栈上,而当您退出该函数时,该条目将从调用堆栈中弹出。因此,基本上,如果一切运行顺利,那么在开始和结束时,调用堆栈将被发现为空。
现在,如果您提供了太多参数或陷入了任何未处理的递归调用中。你会遇到
RangeError:超出最大调用堆栈大小
希望这可以帮助 !
答案for
是正确的,但是如果您确实要使用功能样式避免for
语句-您可以使用以下内容代替表达式:
Array.from(Array(1000000),()=> Math.random());
Array.from()方法从类似于数组或可迭代的对象中创建一个新的Array实例。此方法的第二个参数是一个映射函数,用于调用数组的每个元素。
遵循相同的想法,您可以使用ES2015 Spread运算符将其重写:
[... Array(1000000)]。map(()=> Math.random())
在两个示例中,都可以根据需要获取迭代的索引,例如:
[... Array(1000000)]。map((_,i)=> i + Math.random())
Array(1000000).fill().map(() => Math.random())
也有效
Array.apply
?