21

Does anyone why is the otherwise excellent jQuery.each function is designed differently from the (now) native Array.forEach? F.ex:

var arr = ['abc','def'];
arr.forEach(function(entry, index) {
    console.log(entry); // abc / def
});

This makes absolute sense. But jQuery chose to put the index as first argument:

$.each(arr, function(index, entry) {
   console.log(entry);
});

Does anyone know the reasoning behind this design decision? I have always used $.each extensively, but it always bugged me that the index was the first argument as it is rarely used. I know jQuery implemented a direct reference through this but it’s very confusing if you do:

​var arr = ['abc','def'];
$.each(arr, function() {
    console.log(this === 'abc'); // false both times, since this is a String constructor
});​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

Not that it bothers me so much, I prefer to use native polyfills for the most common new array functions, but I have always been curious about the design decision. Maybe it was made in older times before browsers implemented native forEach and legacy support prevented them from changing it, or...?

Or maybe, it is designed this way because is can be used on native objects too, than then it "makes sense" to put the key before value in the callback...?

Sidenote: I know underscore.js (and maybe other libraries) does it the other way around (more similar to the native function).

David Hellsing
  • 106,495
  • 44
  • 176
  • 212
  • 3
    jQuery's non-conforming version also fails to skip over holes in the Array like the native methods do. – I Hate Lazy Oct 26 '12 at 23:21
  • 1
    As a side note, Underscore's isn't just "more similar to the native function"; it actually uses the native function if present (and if not it uses its implementation which is specifically designed to mirror the native one). – machineghost Oct 26 '12 at 23:32
  • I know it doesn't matter, but I don't think I even agree with `(entry, index)` being in the order they are. I don't know why it wouldn't be `(key, value)`, as `key` is the same as `index` for arrays and you usually reference things as `key, value`. Just my point of view... – Ian Oct 26 '12 at 23:56
  • Functions that operate on a value often accept a single value argument. Because of this, it is very convenient that the native iterators pass the value as the first argument to the callback. This makes functions more useful as they can easily be used stand-alone or as the iterator callback. For example, using the native functions, we can convert an array to a numeric conversion of each member: `arr.map(Number)` Or we can filter it down to a list of values that do not allow a numeric conversion: `arr.filter(isNaN)`. – I Hate Lazy Oct 27 '12 at 02:18

3 Answers3

15

Well, I guess we'd have to ask Mr. Resig himself for an explanation on this. Fact is, that ECMAscript 262 edition 5 wasn't very widespread the time jQuery was designed and developed, so this definitely comes into play. And since it was designed like so, they didn't want to change it later and break all existing code.

In fact, its much more likely that you want to access an element with a higher priority, than the index of it when looping an Array. So, to me there is no reasonable explanation why you would pass in the index first into the callbacks.

Be assured, if jQuery was invented today, they would follow the native implementation behavior.

On the other hand, if it bugs you too much you can simply create a shortcut and use the native Array.prototype.forEach to iterate your jQuery wrapped sets:

var forEach = Function.prototype.call.bind( Array.prototype.forEach );

forEach( $('div'), function( node ) {
    console.log( node );
});

..and for standard Arrays, just go with their native prototype.

while implementation conditional return false/true,we must know what part work in which manner. When you use return false with condition in Array.prototype.forEach it treated as continue, but When you use return false, with condition in $.each it treated as break statement.

var listArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
var arr1 =[];var arr2=[];
var rv = true;
listArray.forEach(function(i, item) {
  if (i == 5) {
    return rv = false;
  }
 arr1.push(i)
  return rv;
});
var listArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
jQuery.each(listArray, function(i, item) {
  if (item == 5) {
    return rv = false;
  }
  arr2.push(i)
});
  console.log("forEach=>"+arr1)
  console.log("$.each=>"+arr2)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Aishwarya
  • 637
  • 9
  • 21
jAndy
  • 231,737
  • 57
  • 305
  • 359
6

This actually got mentioned at the last jQuery conf; unfortunately I don't remember the details, but if you watch enough of the video from it I imagine you can find it (I think it was during Q&A after the keynote, but don't quote me on that). Anyhow someone (I don't think it was Resig himself, but another senior jQuery org member) basically said "jQuery has some things that we all know are problematic ("each" being one of the examples they gave) but there are millions of sites out there that now rely on that problematic behavior so we can't change it now."

So in essence, the answer is that they made a bad decision (Resig is an INCREDIBLE programmer, but even he isn't perfect) and now everyone using jQuery has to suffer for the rest of time because the library maintains relatively high backward compatibility in new versions (and let's face it, that's a good thing: you don't want to have to re-write your whole site every time you upgrade jQuery).

It's also worth mentioning that the Underscore library does have a each method with the same signature as the built-in each. In fact, Underscore's version actually uses the built-in version if present, for better performance, relying only its version if the browser doesn't support the built-in each. Since jQuery objects are just arrays, you can easily use them with _.each, and since Underscore has a lot of other functionality missing from jQuery it's an excellent library to complement jQuery.

machineghost
  • 33,529
  • 30
  • 159
  • 234
  • P.S. You can always use jQuery's plug-in system to "fix" this issue if you want. – machineghost Oct 26 '12 at 23:27
  • P.P.S At the conference they also mentioned that a future version (2.1 IIRC) IS going to break backwards-compatibility, so what I just said isn't 100% true. However, it's only going to break backwards-compatibility in terms of not supporting old IEs anymore; stuff like each isn't going to get fixed. – machineghost Oct 26 '12 at 23:36
1

Well i agree it makes more sense to have the key second but you have to keep in mind $.each can be used with an object or an array and in the case of the object you may want the keys since they have meaning.

prodigitalson
  • 60,050
  • 10
  • 100
  • 114