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.
jQuery.each
andArray.prototype.forEach
are not equivalent.$.each
, and not use the native (and faster)Array.prototype.forEach
?