Did some notice how loop constructs forEach and for/of behave on "added during iteration" items? ForEach ignores the fact that there are newly added items in the array, and for/of alters iterator on the fly and keeps going until no elements are left in the array. What is the reason for this phenomenon, using smart words? 1-st post on the stackoverflow so no judge if explanation is simple.... Thanks
let my_array1 = [1,2,3]
let my_array2 = ["a", "b", "c"]
//extra iteration cycles were not dynamically added. Loop ends after pre-defined # of cycles.
my_array1.forEach((value, index, the_array) => {
if (value == 3){
// my_array1.push(3)
the_array.push(3) // works like so as well
}
})
console.log(`my_array1 after iteration ${my_array1}`)
//extra iteration cycles WERE dynamically added. Infinite loop!!!
for (let value of my_array2){
if (value == "c"){
my_array2.push("c")
}
}
console.log(`my_array2 after iteration ${my_array2}`)
You have to look at the specification (or, for non-native methods, at the documentation for whatever you're using) to see what the behavior is. For example, with Array.prototype.forEach, you can see:
1. Let O be ? ToObject(this value).
2. Let len be ? LengthOfArrayLike(O).
3. If IsCallable(callbackfn) is false, throw a TypeError exception.
4. Let k be 0.
5. Repeat, while k < len,
a. Let Pk be ! ToString(𝔽(k)).
b. Let kPresent be ? HasProperty(O, Pk).
c. If kPresent is true, then
i. Let kValue be ? Get(O, Pk).
ii. Perform ? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »).
d. Set k to k + 1.
6. Return undefined.
As you can see, it iterates while k < len, and len is assigned to at the beginning of the operation, in step 2, so elements added to the array during iteration are not considered. (An element that's changed during iteration will be seen, though - since the values are retrieved with Get(O, Pk) inside each iteration)
for..of, on the other hand, is used on iterables:
Return ? GetIterator(exprValue, iteratorHint).
, and that iterator is then called until the iterator is exhausted
a. Let nextResult be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]])
and array iterators do include items added to the array during iteration , which you can see here. It performs
vi. Set index to index + 1.
at the very end of an iteration, after yielding, and at the beginning of an iteration, looks up the value at that index. As a result, elements added during the invocation of the iterator will be seen in the iterator's results, until the iterator is on the final element in the array and there are no more elements left.
When you call the .forEach method on an array object it sets the end of the loop to the current value of array.length. It would be the same as this:
const arr = [1,2,3,4]
const end = arr.length
for(let i = 0; i < end; i++) {
'''code'''
}
However when you are using the syntax for for...of it is the same as this:
const arr = [1,2,3,4]
for(let i = 0; i < arr.length; i++) {
'''code'''
}
So at every iteration it is checking the length of the array, which will change if you push into the array