It was sort of a surprise for me when I discovered inconsistencies in the most popular JavaScript libraries in how they handle their each and forEach loops. This post compares:
- forEach Loop in Native JavaScript
- each Loop in Lo-Dash
- each Loop in jQuery
- each Loop in Underscore.js
- forEach Loop in Underscore.js
forEach Loop in Native JavaScript
JavaScript Libraries are important (e.g., jQuery, Lo-Dash, Underscore), but in the case of functional loops (forEach
and each
) they create a lot of confusion (for loop can be broken with ‘break’). Let’s inspect the example of native JavaScript code for the forEach
method:
[1,2].forEach(function(v){
alert(v);
return false;
})
This will display us two alert boxes. Try the native JavaScript code yourself in JSFiddle.
This is an expected behavior in most cases because with each iteration we invoke a new function. Unlike the for (var i=0; i<arr.length; i++) {}
code that has no function/iterators.
However, in Lo-Dash and jQuery similar code breaks the loops!
Breaking each Loop in Lo-Dash
The Lo-Dash code with each
produces only one alert:
_.each([1,2],function(v){
alert(v);
return false;
})
Try the above Lo-Dash code yourself in JSFiddle.
Breaking each Loop in jQuery
Likewise, the jQuery each
example shows only the first alert:
$.each([1,2],function(i, v){
alert(v);
return false;
})
Try the jQuery code yourself in JSFiddle.
Non-Breaking each Loop in Underscore.js
To complicate the matter, Underscore.js and Backbone.js remain true to the native JavaScript interpretation.
The Underscore.js each
example that iterates through the each item and doesn’t break:
_.each([1,2],function(v){
alert(v);
return false;
})
Try the Underscore each
method in JSFiddle.
Non-Breaking forEach Loop in Underscore.js
Just for the sake of it, the Underscore forEach()
was tested. It reliably produced the results similar to the native forEach()
: two alerts!
The Underscore forEach()
code:
_.forEach([1,2],function(i, v){
alert(v);
return false;
})
Try the Underscrore.js forEach()
code yourself at JSFiddle.
The Code-Breaking Difference Between Lo-Dash and Underscore
The conclusion of this brief post is that Lo-Dash is not equal to Underscore, unless a special underscore-compatible version is being used. This was kindly pointed out to me by John-David Dalton (@jdalton):
@azat_co Right Lo-Dash isn’t a drop-in unless you use the Lo-Dash.underscore.js build (it’s linked to on the homepage). Also in cdnjs.
— John-David Dalton (@jdalton) November 22, 2013
PS: Underscore.js forEach
is more browser compatible than native forEach
because the latter was a later addition to the JavaScript API and is not supported by older browsers.
Hi Matt, yes, it’s a good point. In the old times, jQuery had to re-invent the standard. That’s another good thing why devs should use jQuery or a similar browser — some fancy new ECMAS APIs might not be available on old browsers (of course if devs had to support old browsers).
As you wrote, “The conclusion of this brief post is that Lo-Dash is not equal to Underscore,” so jQuery is not really the focus, but I imagine the discrepancy between `jQuery.each` and `Array.prototype.forEach` has something to do with the fact that the former was implemented long before the latter was introduced as standard JavaScript.
Yeah, the context is a beast in itself. ;-)
You have mistake in Underscore forEach example. The first parameter is v(value), and second one is i(index). This is also create dissonance to all who start use Underscore after some experience with jQuery, where index goes first.
By the way forEach is just alias to each in Underscore http://underscorejs.org/#each
Also you say nothing about context, I mean this pointer. In jQuery this === v, in Underscore by default this === global object, but you can specify it by putting third parameter to each/forEach function. This is also make a sense.
Thank you. ;-)
I have noticed some weirdness between JQuery and underscore before, however, I could never tell what they were. Excellent post. Simple, basic and straight to the point :)
Every() is useful method to know about, but the point of this article to bring awareness about inconsistent behavior among what seemed to be analogous methods.
Well, sometimes we want to break out of the loop.
Why do you have to add return false? Shouldn’t it work without that?
You don’t really need libs to do breaking loops. There’s Array.prototype.every() that works in same way all those lib functions do. Code like this:
[1,2].every(function(v){alert(v);return false;})
will works in modern browsers AND will break after 1st alert.A good way to stop native forEach iteration could be to throw StopIteration :
window.StopIteration = window.StopIteration || {};
try {
[1,2,3].forEach(function(item) {
console.log(item);
throw StopIteration;
});
}
catch(ex) {
// If the exception is StopIteration, ignore it and continue the exception normally.
if (ex !== StopIteration)
throw ex;
}
Thank you. ;-) Would you care to repost to Italian group?
Google cached original article:
http://i.imgur.com/aCpw5o1.png
The full article is interesting, I wouldn’t have thought there would be differences
Yesterday the post was ending with “code that has no function/iterators” and looked like spam – which is probably the reason it got deleted from NodeJS Italy group.
Maybe it was just a temporary error with your blog
Maybe you should re-read the post then… the point is to compare forEach/each in different libraries, not to compare for and forEach which are completely different things.
Michal, sure we can do a lot of things, but that’s not the point of this post. ;-) The point of this post is to illustrate the breakage of the each/forEach loop in different libraries.
And what’s more, you can shorten the code by passing a function as a parameter:
[1,2].forEach(alert)
This post doesn’t make much sense to me.
And you’ll get the same alerts with both forEach and for.
forEach is blocking and synchronous (and it’s used with synchronous functions).
Check out something like https://github.com/caolan/async