"RangeError: максимальный размер стека вызовов превысило" почему?


Если я

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

на Chrome 33, я получаю

RangeError: Maximum call stack size exceeded

почему?

3 59

3 ответа:

браузеры не могут обрабатывать так много аргументов. См. этот фрагмент кода, например:

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 (по крайней мере, указатели каждого аргумента), поэтому в этом они слишком много аргументов для callstack.

вам нужно понять, что такое стек вызовов.

стек - это структура данных 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, например Нарцисс.

мы реализуем 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 оператор-вместо выражения можно использовать следующее:

массив.от (массив (1000000), () = > математика.random ());

Массив.из() метод создает новый экземпляр массива из массива или итерируемый объект. Второй аргумент этого метода-функция map для вызова каждого элемента массива.

после та же идея, которую вы можете переписать с помощью ES2015 Spread operator:

[...Массив(1000000)].map (() = > математика.random ())

в обоих примерах вы можете получить индекс итерации, если вам нужно, например:

[...Массив(1000000)].map ((_, i) => i + Math.random ())