61

I wonder why frameworks/libraries have their own helpers although they exist natively already.

Let's take jQuery and AngularJS. They have their own each iterator functions:

But we have Array.prototype.forEach.

Similarly,

But we have the JSON.parse() function in vanilla JavaScript.

Peter Mortensen
  • 1,050
  • 2
  • 12
  • 14
Cihad Turhan
  • 673
  • 5
  • 6
  • 75
    As someone who remembers the bad old days of web development, this question makes me cry. – josh3736 Apr 08 '14 at 03:57
  • 3
    @josh3736 At least you don't still have to support IE6 ('tho, luckily only in a "make it work, it can look like crap" way) – Izkata Apr 08 '14 at 19:16
  • 12
    `jQuery.each` and `Array.prototype.forEach` are not equivalent. – zzzzBov Apr 08 '14 at 21:01
  • 3
    What you should ask yourself is: how much of the functions found in vanillaJS now, originated from toolkits like jQ and the like? The answer is: _many_. This does beg the question: why are we still using them? Why include jQuery's `$.each`, and not use the native (and faster) `Array.prototype.forEach`? – Elias Van Ootegem Apr 10 '14 at 14:29
  • 1
    @josh3736 It's ok bro... http://i.stack.imgur.com/HJs4V.jpg – Crono Apr 10 '14 at 18:47

3 Answers3

94

Because when those libraries were written, some major browsers did not support those features. Once written and used, these features cannot be removed from these libraries without breaking many applications.

(In this case, "major browser" means a browser that still has large market share, which includes older versions of browsers like Internet Explorer, where large number of users don't necessarily upgrade to the latest version.)

Gort the Robot
  • 14,733
  • 4
  • 51
  • 60
  • 44
    $('marquee').each(function() { $(this).append($('', { src: "good-answer.mp3"} )); }); – Pierre Arlaud Apr 08 '14 at 08:22
  • 1
    While I like your answer in general and I think it is true for jQuery, I very much doubt that browsers did not support this when Angular was written. After all, it is pretty new. – dirkk Apr 08 '14 at 08:44
  • 37
    @dirkk It's not that recent browsers don't support it. It's that not everyone is lucky enough to have an audience that uses a recent browser. – George Reith Apr 08 '14 at 11:17
  • 2
    @GeorgeReith True, that is the case and perfectly reasonable. However, that wasn't my point: The first sentences in the answer lead to the immediate conclusion that some major browsers did not support `forEach` when AngularJS was written, which is simply not true. However, it is perfectly reasonable to implement these constructs because you want to support older browsers which in fact did not have these. – dirkk Apr 08 '14 at 11:23
  • 14
    `Array.prototype.forEach` iterates only over arrays - both library iterator functions can iterate over arrays or object. – JoeG Apr 08 '14 at 12:16
  • Then why jQuery-2 supports these methods? jQuery-2 will work only latest browsers, right? –  Apr 08 '14 at 12:44
  • 3
    The functions are there to support old browsers and to support old code that calls the library and the programmer doesn't want to rewrite. Even if you've dropped support for IE 6 you probably still have some JavaScript in use written when you did need to support ancient copies of IE. – Michael Shopsin Apr 08 '14 at 14:19
  • I've updated to describe what is meant by major browser just to make it clear that the issue is what browsers people are using rather than what the latest browser versions support. – Gort the Robot Apr 08 '14 at 23:24
  • 6
    Many of this functions (e.g. jQuery.parseJSON()) just check if the browser supports it and then sets itself to the browser method, and only uses an alternative on non compatible browsers! – Josef Apr 09 '14 at 14:25
35

Because different browsers have different implementations and features baked in their JavaScript engine. The same "vanilla-JS" code could run differently on two different browsers, or even two different versions of the same browser.

The abstraction layer provided by popular JS libraries is a way around this. Behind the scenes, it works around the different browsers capacities and limitations and offers a unified, easy to use API on top of those. This, in turn, allows common operations such as getting a DOM object or fetching JSON data to be consistent, efficient and browser-agnostic.

This makes life a lot easier for developers who can now focus on what code should do, rather than how it should be written to work with browser X or Y.

Crono
  • 1,617
  • 15
  • 24
  • 2
    The behavior of "core JS" is well-specified and tested across all browsers. – Domenic Apr 09 '14 at 18:32
  • 2
    @Domenic Syntax aside, javascript implementations do differ from browser to browser. There are properties, methods, events and features you will find in just a few browsers only, or even just in one sometimes. – Crono Apr 09 '14 at 18:39
  • 1
    Yes, browsers have nonstandard features. That has nothing to do with the standard features discussed in this question. – Domenic Apr 10 '14 at 16:54
  • 8
    @Domenic If by "standard features discussed in the question" you mean the `Array.prototype.forEach` and `JSON.parse` functions, a quick search on Google will show you that you are wrong. `JSON` object was not supported on IE7 and `forEach` wasn't defined on some Opera versions. Libraries such as jQuery however did knew about these limitations and worked around them behind the scenes. So I think my answer does stand. – Crono Apr 10 '14 at 17:24
28

1. Backwards compatibility

JavaScript is an implementation of ECMAScript. Most of those functions were introduced in ECMAScript 5 (ES5) however many older browsers which still have a significant enough share of the market do not support these functions (see ECMAScript 5 compatibility table), the most notable of these being IE8.

Generally libraries will revert to the native implementation if it exists otherwise use their own polyfill, for example let's look at AngularJS's implementation (angular.js L203-257):

function forEach(obj, iterator, context) {
  var key;
  if (obj) {
    if (isFunction(obj)){
      for (key in obj) {
        // Need to check if hasOwnProperty exists,
        // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
        if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
          iterator.call(context, obj[key], key);
        }
      }
    } else if (obj.forEach && obj.forEach !== forEach) {
      obj.forEach(iterator, context);
    } else if (isArrayLike(obj)) {
      for (key = 0; key < obj.length; key++)
        iterator.call(context, obj[key], key);
    } else {
      for (key in obj) {
        if (obj.hasOwnProperty(key)) {
          iterator.call(context, obj[key], key);
        }
      }
    }
  }
  return obj;
}

The following lines check whether the forEach method exists on the object and whether it is the AngularJS version or not. If not it uses the already specified function (the native version):

} else if (obj.forEach && obj.forEach !== forEach) {
  obj.forEach(iterator, context);
}

2. Convenience

In native JavaScript Array.prototype.forEach is a method exclusive to an instance of Array, however most any Object is iterable too.

For this reason many library creators make their functions polymorphic (able to accept multiple types as input). Let's take the AngularJS code above and see what inputs it accepts:

Functions:

if (isFunction(obj)){
  for (key in obj) {
    // Need to check if hasOwnProperty exists,
    // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
    if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
      iterator.call(context, obj[key], key);
    }
  }

Arrays (with native forEach support):

} else if (obj.forEach && obj.forEach !== forEach) {
  obj.forEach(iterator, context);

Array-like objects including Array (without native forEach support), String, HTMLElement, Object with a valid length property:

} else if (isArrayLike(obj)) {
  for (key = 0; key < obj.length; key++)
    iterator.call(context, obj[key], key);

Objects:

} else {
  for (key in obj) {
    if (obj.hasOwnProperty(key)) {
      iterator.call(context, obj[key], key);
    }
  }
}

Conclusion

As you can see AngularJS will iterate over most any JavaScript Object, although it works in the same way as the native function it accepts far more different types of input and thus is a valid addition to the library as well as a way of bringing ES5 functions to legacy browsers.

George Reith
  • 501
  • 3
  • 5
  • 2
    You may want to update your link to point to a particular commit (e.g. [angular.js L203-257](https://github.com/angular/angular.js/blob/4b1695ec61aac8de7fcac1dfe8b4b420f9842c38/src/Angular.js#L203-L257)) for future reference once `master` changes. – Whymarrh Apr 12 '14 at 00:27