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