2014-11-30 4 views
1

Мне нужно было вставить что-то в массив, находясь в середине Array.forEach. Я хотел проверить, как JavaScript обрабатывает эти ситуации. Я ожидал чего-то, включая бесконечную петлю. Но результат для меня очень странный. Можете ли вы объяснить, что происходит здесь:Изменение массива внутри forEach

var A = [0, 1, 2, 3, 4, 5, 6, 7] 
 
A.forEach(function(x) { 
 
    if (x == 3) A.splice(1, 0, 'new') 
 
}) 
 
document.write(JSON.stringify(A))

+2

алгоритм 'Array.forEach' доступен в [спецификация языка] (http://www.ecma-international.org/ecma-262/5.1/# втор-15.4.4.18). (Или, чтобы увидеть это в JS, проверьте [polyfill at MDN] (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach#Polyfill).) – nnnnnn

ответ

3

Из наблюдений с большим количеством промежуточных writeln с:

  1. Петля forEach смотрит на каждого индекса оригинальный только один раз - поэтому он должен был сохранить длину массива в некоторой временной переменной.

  2. Вставка splice «новая» в том же месте: после первого элемента.

  3. Это происходит только после просмотра «3» на 4-й позиции.

  4. Таким образом, на 3-й итерации, после первого введения, ваш массив выглядит следующим образом:

    [ 0, "new", 1, 2, 3, 4, 5, 6, 7 ] 
    

    и forEach итератор на элемент 4 - который является «3» снова.

  5. .. и так «новые» вставлены для остатков оригинала длина массива.

Писатель кода forEach должен считать, что кто-то меняет массив за кулисами. Немного более безопасный способ мог бы сделать копию исходного массива, но я думаю, что для этого понадобится копия глубокой (так как вы не можете заранее знать, насколько глубоко это произойдет), и это, возможно, привело к к большому количеству копий для того, что кажется краевым случаем.

3

То, что вы получаете, - это ожидаемое поведение, и хотя это не настоящий бесконечный цикл - это непроизводительный цикл.

Вы добавляете элемент перед текущим индексом. Это приводит к смещению текущего значения, поэтому вы начнете получать x == 3 на каждой итерации.

Это все помещения для бесконечного цикла, однако JS forEach накладывает на него потолок, а этот потолок представляет собой количество элементов, присутствующих в вашем исходном массиве. Независимо от того, как вы манипулируете массивом, он не может работать более 8 раз, что является исходной длиной.

Вывод этого кода показывает лучшее, что происходит:

var A = [0, 1, 2, 3, 4, 5, 6, 7] 
    A.forEach(function(x, index) { 
    document.writeln('A[' + index + '] = ' + x + '<br/>'); 
    if (x == 3) { 
     A.splice(1, 0, 'new') 
    } 
    }) 
    document.write(JSON.stringify(A)); 

Выход:

A[0] = 0 
A[1] = 1 
A[2] = 2 
A[3] = 3 
A[4] = 3 
A[5] = 3 
A[6] = 3 
A[7] = 3 
[0,"new","new","new","new","new",1,2,3,4,5,6,7] 
+1

_ «Итак, вы на самом деле заканчивается бесконечным циклом "_ - Нет, вы этого не делаете. Алгоритм метода '.forEach()' явно определен, чтобы отметить исходную длину массива перед циклом и вызвать обратный вызов для каждого определенного индекса, который меньше этой длины. – nnnnnn

+0

Здесь мы рассуждаем о семантике. Бесконечный цикл также можно определить как непроизводительный цикл, который, безусловно, имеет место здесь. Даже цикл while (true) {} не бесконечен, потому что он заканчивается, когда у вас заканчивается память. –

+1

Нет, это не просто семантика. Бесконечный цикл - это, по сути, тот, в котором конечное условие никогда не встречается, так как оно выполняется «навсегда» - которое может или не может быть остановлено средой выполнения, а не буквально никогда не заканчиваться. 'while (true) {}' не будет исчерпаться, потому что на каждой итерации не требуется дополнительная память. Гарантируется, что конечное условие алгоритма '.forEach()' выполняется независимо от того, как вы манипулируете массивом на любой заданной итерации, поэтому оно не соответствует определению «бесконечный». – nnnnnn

1

Хорошо так что давайте делать это по шагам:

  1. Ваш цикл итерацию нормально до элемента "3"

  2. На элементе «3» ваша петля проходит мимо «новой» в первой позиции вашего массива: [0, новый, 1, 2, 3, 4, 5, 6, 7], но все ваши элементы перемещаются на одной позиции справа, так что на следующей итерации вашей четвертой части цикла снова «3», потому что она была перемещена.

  3. Он повторяет шаг 2 до последнего 8-го элемента и более 4 раз помещает «новое» в первую позицию массива. если вы хотите вставить "новый" один раз, так что это нравится:

    A.forEach (функция (х) {
    если (х == 3) {A.splice (1, 0, 'новый'); перерыва;}} )

Смежные вопросы