90

I've seen plenty of questions that suggest using:

for (var i = 0; i < myArray.length; i++){ /* ... */ }

instead of:

for (var i in myArray){ /* ... */ }

for arrays, due to inconsistent iteration (see here).


However, I can't seem to find anything that seems to prefer the object oriented loop:

myArray.forEach(function(item, index){ /* ... */ });

Which seems way more intuitive to me.

For my current project, IE8 compatibilty is important, and I'm considering using Mozilla's polyfill, however I'm not 100% sure how this will work.

  • Are there any differences between the standard for loop (the first example above) and the Array.prototype.forEach implementation by modern browsers?
  • Are there any difference between modern browser implementations and Mozilla's implementation linked to above (with special regard to IE8)?
  • Performance is not as much of an issue, just consistency with which properties are iterated over.
Community
  • 1
  • 1
Michael Lewis
  • 4,252
  • 6
  • 28
  • 39
  • 6
    It's not possible to `break` out of `forEach`. But a big advantage is to create a new scope with the function. With the polyfill you shouldn't have any problems (at least I haven't encountered any). – hgoebl May 12 '14 at 16:32
  • The problems you can have with older IE, is not the shim itself, but the broken array constructor/literal wrt `holes` where `undefined` should be and other broken methods, like `slice` and `hasOwnProperty` wrt to arraylike DOM objects. My testing and [`es5 shim`](https://github.com/es-shims/es5-shim) have shown such shimmed methods compliant with the specification (not tested MDN's shim). – Xotic750 May 12 '14 at 16:57
  • 1
    And wrt to breaking out of a `for` loop, that is what [`some`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/some) is for. – Xotic750 May 12 '14 at 16:59
  • 1
    "However, I can't seem to find anything that seems to prefer the object oriented loop:" I would rather call it functional way vs imperative. – Memke Nov 29 '16 at 10:16
  • You can use [`Array.find()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find) to break out of the loop after finding a first match. – Mottie Dec 25 '16 at 14:44
  • https://thejsguy.com/2016/07/30/javascript-for-loop-vs-array-foreach.html – doubleOrt Dec 27 '17 at 22:30

3 Answers3

125

The most substantive difference between the for loop and the forEach method is that, with the former, you may break out of the loop. You can simulate continue by simply returning from the function passed to forEach, but there is no way to stop looping altogether.

Aside from that, the two accomplish effectively the same functionality. Another minor difference involves the scope of the index (and all containing variables) in the for loop, due to variable hoisting.

// 'i' is scoped to the containing function
for (var i = 0; i < arr.length; i++) { ... }

// 'i' is scoped to the internal function
arr.forEach(function (el, i) { ... });

However, I find that forEach is much more expressive—it represents your intent to iterate through each element of an array, and it provides you with a reference to the element, not just the index. Overall, it mostly comes down to personal taste, but if you can use forEach, I would recommend using it.


There are a few more substantial differences between the two versions, specifically regarding performance. In fact, the simple for loop performs considerably better than the forEach method, as demonstrated by this jsperf test.

Whether or not such performance is necessary for you is up to you to decide, and in most cases, I would favor expressiveness over speed. This speed difference is likely due to the minor semantic differences between the basic loop and the method when operating on sparse arrays, as noted in this answer.

If you don't need the behavior of forEach and/or you need to break out of the loop early, you can use Lo-Dash's _.each as an alternative, which will also work cross-browser. If you're using jQuery, it also provides a similar $.each, just note the differences in arguments passed to the callback function in each variation.

(As for the forEach polyfill, it should work in older browsers without problems, if you choose to go that route.)

Community
  • 1
  • 1
Alexis King
  • 43,109
  • 15
  • 131
  • 205
  • 1
    It is worth noting that the library versions `each` do not behave in the same manner as the ECMA5 specification of `forEach`, they tend to treat all arrays as dense (to avoid IE bugs, provided that you are aware). Can be a "gotcha" otherwise. As a reference https://github.com/es-shims/es5-shim/issues/190 – Xotic750 May 12 '14 at 17:01
  • 7
    Also there are some prototype methods that allow you to break such as `Array.prototype.some` which will loop until you return a truthy value or until it has looped completely through the array. `Array.prototype.every` is similar to `Array.prototype.some` but stops if you return a falsy value. – axelduch May 12 '14 at 17:15
  • If you want to see some test result on different browsers and such shims, you can take a look here, https://ci.testling.com/Xotic750/util-x – Xotic750 May 12 '14 at 17:16
  • Also, using Array.prototype.forEach would put your code at the mercy of the extension. For example, if you are writing a code to be embedded into a page, there is a chance that Array.prototype.forEach is overwritten with something else. – Marble Daemon Aug 19 '16 at 04:32
  • 2
    @MarbleDaemon The chance of that is so minuscule that it is effectively impossible and therefore negligible. Overwriting `Array.prototype.forEach` with some non-compatible version would have the potential to break so many libraries that avoiding it for that reason wouldn’t help, anyway. – Alexis King Aug 19 '16 at 04:54
  • @AlexisKing A risk is still a risk. Especially when you are asked to write some code that would be embedded to an unknown page. You have no clue of what would that page contain already. Then why take that risk for no gain? Unless there is a higher risk using standard for loop. – Marble Daemon Aug 20 '16 at 16:50
12

You can use your custom foreach function which will perform much better then Array.forEach

You should add this once to your code. This will add new function to the Array.

function foreach(fn) {
    var arr = this;
    var len = arr.length;
    for(var i=0; i<len; ++i) {
        fn(arr[i], i);
    }
}

Object.defineProperty(Array.prototype, 'customForEach', {
    enumerable: false,
    value: foreach
});

Then you can use it anywhere like the Array.forEach

[1,2,3].customForEach(function(val, i){

});

The only difference it is 3 times faster. https://jsperf.com/native-arr-foreach-vs-custom-foreach

UPDATE: In new Chrome version the performance of .forEach() was improved. However, the solution can give the additional performance in other browsers.

JSPerf

Seagull
  • 3,319
  • 2
  • 31
  • 37
5

It is suggested by many developers (e.g. Kyle Simpson) to use .forEach to indicate that the array will have a side effect and .map for pure functions. for loops fit well as a general-purpose solution for known number of loops or any other case that doesn't fit as it is easier to communicate because of its broad support across the majority of programming languages.

e.g.

/* For Loop known number of iterations */
const numberOfSeasons = 4;
for (let i = 0; i < numberOfSeasons; i++) {
  //Do Something
}

/* Pure transformation */
const arrayToBeUppercased = ['www', 'html', 'js', 'us'];
const acronyms = arrayToBeUppercased.map((el) => el.toUpperCase));

/* Impure, side-effects with .forEach */
const acronymsHolder = [];
['www', 'html', 'js', 'us'].forEach((el) => acronymsHolder.push(el.toUpperCase()));

Convention wise, this seems best, however the community hasn't really settled on a convention on the newer iteration protocol for in loops. Generally, I think it's a good idea to follow the FP concepts that the JS community seems to be open to adopting.

steviesh
  • 1,740
  • 6
  • 19
  • 33