You should use iterator methods for clarity and fewer side effects.
When I code I avoid using “for” loops for several reasons.
- It is extremely easy to have “off by 1 error”, especially when filtering an array.
- Iteration variable just adds more noise to the scope
- Most importantly, one has to read through the iteration code to understand what it does.
I prefer built-in Array or utility library iterators, like lodash or Ramda. Not only the code is shorter and has fewer variables, it expresses the intent a lot clearer. One can almost read the iteration as plain English sentence in most cases
1 |
var doubled = numbers.map(double);
|
2 |
var longNames = names.filter(minLength);
|
3 |
var totalAge = people.reduce(...);
|
There are only 3 cases when doing the iteration
- New array is created by transforming each value (
.map
) - New array is created from items that pass the callback (
.filter
) - Single value is computed from the array (
.reduce
,.some
,.every
)
In each case, one can understand the data flow much faster by looking at the iteration method, rather than looking from for (var k = ; ...) { ... }
code.
Banning for loops
One can easily ban for(...)
statements in the existing application using my custom rule no-for-loop
for eslint. The rule is available with a few of my rules in bahmutov/eslint-rules. Just include the rules folder and set no-for-loops:2
in your eslint rules file.
Banning all for loops at once is surely to upset people. Thus I advice you to generate warning for the current code, but ban it for new or modified code. One can easily apply different eslint rules for old / new source files using aged Grunt plugin.
Finally, if you must allow a specific for()
loop in your code, use /* eslint no-for-loops:0 */
right above the loop to allow only this specific instance.
Extra details
- To prevent accidental original array modification inside
.map
for example, you should look into using immutable data structures, read Avoid side effects with immutable data structures. - You can easily iterate over objects, not just arrays. See Object iterators.
- Array iterator methods are slower than
for()
loops, but you can optimize their performance, see Unorthodox Performance talk by Lodash’s author. - If you need the iteration index (for example when merging two arrays), it is passed as second argument to the iteration
1 |
var people = names.map( function (name, k) {
|
2 |
return {
|
3 |
name: name,
|
4 |
age: ages[k]
|
5 |
};
|
6 |
}); |