Skip to content

Instantly share code, notes, and snippets.

@sammoore
Last active December 16, 2016 20:28
Show Gist options
  • Save sammoore/c7fafb0c508ead14eff79fecc13e884b to your computer and use it in GitHub Desktop.
Save sammoore/c7fafb0c508ead14eff79fecc13e884b to your computer and use it in GitHub Desktop.
A forEach function with as little assumptions as possible to iterate over classical collections (array-like objects that must be manually iterated, e.g. integer subscripting or through a `.item(i)` function). Akin to Underscore's _.each/forEach for array-like types only, but with a tidbit more control over iteration. Tested with ES5, but theoret…
/**
* Iterates over `list`, executing `callback` with each element in the collection.
* @requires nothing?
*
* Designed for array-like objects that are not iterable.
* @see https://dom.spec.whatwg.org/#old-style-collections
*
* Due to some versions of Internet Explorer, if `list.item` is defined, it will be treated as a
* function and used rather than integer subscripting. This behavior can be overriden via `useItem`.
* @see http://stackoverflow.com/a/10740835
*
* @param {Object} list The object with integer indicies, through subscripting or a standard `.item` method, which will be iterated.
* @param {Function} callback A callback function to be called for each element up to `len`.
* @param {any} thisArg Optional argument which will bind to the callback's `this` value if provided. Use `null` to prevent binding.
* @param {Number} len Optional argument stating the length of the enumerable. Defaults to `list.length`.
* @param {Boolean} useItem Optional argument stating whether to use `list.item(i)` or integer subscripting.
* @throws {TypeError} if `callback` is not a function
* @throws {TypeError} if length cannot be coerced via `list.length` or `len`
* @returns {undefined}
*/
function forEach(list, callback, thisArg, len, useItem) {
'use strict';
if (typeof list != 'object' && typeof list != 'array') {
throw new TypeError('forEach requires list to be an array or object.');
}
if (typeof callback != 'function') {
throw new TypeError('forEach requires callback to be a function');
}
if (typeof len == 'undefined' && typeof list.length == 'undefined') {
throw new TypeError('forEach requires either len or list.length to be defined');
}
thisArg = thisArg || null;
len = +(len || list.length);
var useItem = !!useItem || typeof list.item != 'undefined';
var getter = useItem // for performance, make a getter, rather than checking each time.
? function(i) { return list.item(i); }
: function(i) { return list[i]; };
var i = 0;
while (i < len) {
callback.call(thisArg, getter(i));
}
return undefined;
};
NodeList.prototype.forEach = NodeList.prototype.forEach || function(callback, argument) {
'use strict';
return forEach(this, callback, argument);
};
HTMLCollection.prototype.forEach = HTMLCollection.prototype.forEach || function(callback, argument) {
'use strict';
return forEach(this, callback, argument);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment