Can't JavaScript foreach use break/continue? In the same way, the for loop does not work

Can't JavaScript foreach use break/continue? In the same way, the for loop does not work

Today, I learned and discussed the foreach/map function when discussing a JavaScript asynchronous problem with my friends in the group. Of course, the focus is on asynchrony.

Here, I will analyze the details in combination with my own understanding and polyfill implemented on MDN.

for loop vs forEach

Here, we will simply compare the for loop and forEach. The input and conditions are the same.

for loop

Normally, most people use the for loop like this:

const arr = [1, 2, 3, 4, 5];

for (let i = 0; i < arr.length; i++) {
  console.log(arr[i]);
}

Take the above array as an example. It is an ascending array. Assuming that the requirement is to print all numbers below 3 (including 3), you can use the break keyword to jump out of the loop to improve performance:

const arr = [1, 2, 3, 4, 5];

for (let i = 0; i < arr.length; i++) {
  console.log(arr[i]);
  if (arr[i] >= 3) break;
}

In this way, the console will only output 1, 2 and 3, not more than 3. For the traditional for loop, you need to control the index and develop your own to achieve growth, but it is a little troublesome to use.

forEach

Therefore, in addition to the traditional for loop, ES5 also provides more convenient iterative functions such as forEach() and map().

Take forEach() as an example to complete the output of all values of the array, which can be realized as follows:

const arr = [1, 2, 3, 4, 5];

arr.forEach((el, index) => {
  console.log(el, index);
});

Compared with the traditional for loop, foreach () obviously has less code and is more concise and clean to write. However, if you have the same requirement, that is, only print all numbers below 3 (including 3), there is no way to terminate the iteration like the for loop by using forEach():

const arr = [1, 2, 3, 4, 5];

arr.forEach((el, index) => {
  console.log(arr[i]);
  // Will report an error
  if (el >= 3) break; // SyntaxError: Illegal break statement
});

In this case, using the return keyword will not produce any effect. Using break and continue will result in SyntaxError.

Therefore, I have seen some articles before that recommend to forcibly terminate the program by throwing exceptions, and then use try/catch on the outside to catch the thrown exceptions, so that the program will not be terminated - this is actually not a good habit. This code also has logical problems.

Moreover, in fact, when the same writing method is implemented, using the for loop will have the same consequences.

Callback functions are the root of the problem

The complete syntax of forEach is actually as follows:

arr.forEach(callback(currentValue [, index [, array]])[, thisArg])

Explain it in Chinese as follows:

  • forEach accepts a callback function as a necessary parameter

    The callback function will accept the following three parameters:

    • currentValue

      The value currently being manipulated

    • index

      Index of the currently operated value, optional

    • array

      The array that the forEach() method is operating on, optional

  • forEach accepts a thisArg as an optional parameter

    thisArg is used as this in the callback function

In other words, when forEach is called, the callback function is actually performing real operation. So forEach can be rewritten as follows:

function cb(currentValue, index, array) {
  console.log(currentValue);
  // Break & continue certainly won't work
  // Because cb is not in the circulation

  // return will only stop cb
  // So for forEach, it will end the current iteration and enter the next loop
  return;
}

arr.forEach(cb);

And for each polyfill The same is true:

// Production steps of ECMA-262, Edition 5, 15.4.4.18
// Reference: https://es5.github.io/#x15.4.4.18

if (!Array.prototype['forEach']) {
  Array.prototype.forEach = function (callback, thisArg) {
    // Some exception handling and English remarks are omitted, leaving only the code for handling callback

    var T, k;

    var O = Object(this);

    var len = O.length >>> 0;

    if (arguments.length > 1) {
      T = thisArg;
    }

    k = 0;

    while (k < len) {
      var kValue;
      if (k in O) {
        kValue = O[k];
        // T is this. With parameters, it is thisArg
        // kValue is currentValue
        // k is index
        // O is array
        callback.call(T, kValue, k, O);
      }
      k++;
    }
  };
}

Using cb, the for loop will also report an error

The effect of writing the callback function in the for loop is the same:

const arr = [1, 2, 3, 4, 5];

function cb(currentValue, index, array) {
  console.log(currentValue);
  // Using break/continue here will also report an error

  // return terminates the function in advance. No other operations will be performed
  // This is equivalent to using continue directly within a for loop
  if (index >= 3) {
    return;
  }
  console.log('forever<3', index);
}

for (let i = 0; i < arr.length; i++) {
  cb(arr[i], i);
}

The output result is:

notes>node test.js
1
 forever<3 0
2
 forever<3 1
3
 forever<3 2
4
5

About map

map and forEach are also products of ES5, and their implementation methods are quite similar, so the principle of exceptions is the same.

The only difference is that the map adds an array to the implementation, and the final result also returns an array. The simplified version is about:

// Array. Is not rewritten here prototype. Map, which directly accepts an array as a parameter
const map = (arr, cb) => {
  const returnVal = [];
  for (let i = 0; i < arr.length; i++) {
    const el = arr[i];
    returnVal.push(cb(el));
  }
  return returnVal;
};

// Test code
const arr = [1, 2, 3, 4, 5];
// The implementation of the main content is still in the callback function, so similarly, using break/continue has no effect
// A return in advance simply pushes an undefined value into the return array in the map
console.log(map(arr, (val) => val ** 2)); // [ 1, 4, 9, 16, 25 ]

A substitute for forEach

ES6 launched a for For the syntax of, the internal implementation principle is implemented by implementing iteratable. Therefore, continue, break, return and yield can be used internally. The effects are as follows:

for (const val of arr) {
  if (val % 2 === 0) continue;
  console.log(val ** 2);
}

console.log('-----------');

for (const val of arr) {
  if (val > 2) break;
  console.log(val);
}

console.log('-----------');

for (const val of arr) {
  if (val === 1) return;
  console.log(val); // There will be no output
}

The output result is:

1
9
25
-----------
1
2
-----------

Keywords: Javascript array es5

Added by TecBrat on Sun, 16 Jan 2022 18:57:11 +0200