Если я запустил
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
Почему?
Браузеры не могут обрабатывать множество аргументов. См. Этот пример, например:
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 (по крайней мере, указатели на каждый аргумент), поэтому в них слишком много аргументов для вызова.
Вам нужно понять, что такое стек вызовов.
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
, локальные переменные a
недоступны там из-за роли определения роли Javascript, но механизм Javascript должен помнить локальные переменные и аргументы, поэтому он будет вставлять их в столбец вызова. Скажем, вы используете механизм JavaScript с языком JavaScript, например Narcissus.
Мы реализуем массив 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, называется фрейм стека, в котором содержатся аргументы, локальные переменные и адрес функции.
Ответ с for
верен, но если вы действительно хотите использовать функциональный стиль, избегающий инструкции for
, вы можете использовать следующее вместо своего выражения:
Array.from(Array (1000000),() = > Math.random());
Метод Array.from() создает новый экземпляр массива из объекта, подобного массиву или итерации. Второй аргумент этого метода - это функция отображения для каждого элемента массива.
Следуя той же идее, вы можете переписать ее с помощью ES2015 Spread operator:
[... Array (1000000)]. map (() = > Math.random())
В обоих примерах вы можете получить индекс итерации, если вам нужно, например:
[... Array (1000000)]. map ((, i) = > я + Math.random())