简体   繁体   中英

Modifying array within forEach

I needed to insert something into an array while being in a middle of Array.forEach. I wanted to check how JavaScript handles those situations. I expected anything, including endless loop. But the result is very strange to me. Can you explain what is going on here:

 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)) 

From observing with lots of intermediate writeln s:

  1. The forEach loop looks at each original index only once -- so it must have stored the length of the array in some temporary variable.

  2. The splice line inserts "new" at the same place: after the first element.

  3. This happens only after seeing the "3" at the 4th position.

  4. So at the 3rd iteration, after the first insertion, your array looks like this:

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

    and the forEach iterator is at element 4 -- which is the "3" again.

  5. .. and so "new" gets inserted for the remainders of the original array length.

The writer of the forEach loop code must have considered someone changing the array behind the scenes. A slightly safer way could have been to make a copy of the original array, but I think that would need a deep copy (sich you cannot know in advance how deep the change is going to take place), and that may have led to lots of copying for what seems an edge case.

What you get is the expected behavior and while it's not a true endless loop - it's an unproductive loop.

You are adding an element before the current index. This makes the current value to shift so you will start getting x == 3 on each iteration.

This are all premises for an endless loop however JS forEach puts a ceiling on it, and that ceiling is the number of elements present in your original array. No matter how you manipulate the array, it cannot run for more than 8 times, which is the original length.

The output of this code shows best what is happening:

  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));

Output:

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]

Ok so let's do it by steps:

  1. Your loop iterates normally until element "3"

  2. On the element "3" your loop past "new" on the first position of your array like: [0, new, 1, 2, 3, 4, 5, 6, 7], but all your elements are moving on the one position to the right, so on the next iteration of your loop 4th element again "3" because it was moved.

  3. He repeats step 2 until the last 8th element, and more 4 times put "new" on the first position of array. if you want to paste "new" one time so do it like:

    A.forEach(function(x) {
    if (x == 3) {A.splice(1, 0, 'new'); break;} })

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM