Question

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.

Was it helpful?

Solution

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.)

OTHER TIPS

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.

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.

Licensed under: CC-BY-SA with attribution
scroll top