The latest Ecma 5 specification adds various interesting methods to the JavaScript Array prototype. Sadly, most browsers do not implement these methods yet, or at least not all of them. Internet Explorer is usually the worst culprit, while the other browsers are still in the process of implementation. So what do you do when you want to use some of these methods on an (older) browser that doesn’t support them yet?

JavaScript utility libraries like jQuery and Prototype implement some of these methods, but more typically a set of different methods. If you would like to standardize your methods with the Ecma 5 specification, then you’ll need to do more work.

As it happens, JavaScript allows native objects like Array to be extended by extending their prototypes. If you would like to add a method to the Array prototype, you would do this:

Array.prototype.hello = function (str) {
  console.log ("Hello " + str);

After this code is executed, any other code can now call the hello method on any array. There is no need to instantiate a special array class, or to make any modifications to the code. This is the magic of prototypes:

var a = [1,2,3];
a.hello("world");

Adding methods to a prototype in order to extend them to satisfy the Ecma standard is called polyfilling. In this article, we present a set of methods to add to the Array prototype to complete the Ecma 5 specification. All methods are defined within an immediately invoked function expression (IFFE) so as not to create namespace conflicts with other code:

(function () {
  "use strict";
  if (!Array.myMethod) {
    Array.myMethod= function (arg) {
      // method code
    };
  }
}());

isArray

Available since: Ecma 5.1, Chrome 5, Firefox 4.0, IE 9, Opera 10.5, Safari 5

Returns true if arg is an array.

if (!Array.isArray) {
  Array.isArray = function (arg) {
    return Object.prototype.toString.call(arg) === '[object Array]';
  };
}

every

Available since: Ecma 5.1, Chrome, Firefox 1.5, IE 9, Opera, Safari

The every method tests whether all elements in the array pass the test implemented by the provided function. The every method executes the provided callback function once for each element present in the array until it finds one where callback returns a falsy value (a value that becomes false when converted to a boolean). If such an element is found, the every method immediately returns false. Otherwise, if callback returned a true value for all elements, every will return true. The callback is invoked only for indexes of the array which have assigned values; it is not invoked for indexes which have been deleted or which have never been assigned values.

if (!Array.prototype.every) {
  Array.prototype.every = function (callbackfn, /*optional*/ thisArg) {
    var k, len;
    
    // Method cannot be run on an array that does not exist.
    if (this == null) {
      throw new TypeError('this is null or not defined');
    }
    
    // The callback must be a function.
    if (typeof callbackfn !== 'function') {
      throw new TypeError();
    }
    
    // Loop through array and call callback for each element.
    len = this.length;
    k = 0;
    while (k < len) {
      if (k in this) {
        if (!callbackfn.call(thisArg, this[k], k, this)) {
          return false;
        }
      }
      k = k + 1;
    }
    return true;
  };
}

filter

Available since: Ecma 5.1, Chrome, Firefox 1.5, IE 9, Opera, Safari

The filter method creates a new array with all elements that pass the test implemented by the provided function.

if (!Array.prototype.filter) {
  Array.prototype.filter = function (callbackfn, /*optional*/ thisArg) {
    var k, len, result = [];
    
    // Method cannot be run on an array that does not exist.
    if (this == null) {
      throw new TypeError('this is null or not defined');
    }
    
    // The callback must be a function.
    if (typeof callbackfn !== 'function') {
      throw new TypeError();
    }
    
    // Loop through array.
    len = this.length;
    k = 0;
    while (k < len) {
      if (k in this) {
        // For each element, if callback returns truthy, add it to
        // result array.
        if (callbackfn.call(thisArg, this[k], k, this)) {
          result.push(this[k]);
        }
      }
      k = k + 1;
    }
    return result;
  };
}

foreach

Available since Ecma 5.1, Chrome, Firefox 5.1, IE 9, Opera, Safari

The forEach method executes a provided function once per array element.

if (!Array.prototype.forEach) {
  Array.prototype.forEach = function (callbackfn, /*optional*/ thisArg) {
    var k, len;
    
    // Method cannot be run on an array that does not exist.
    if (this == null) {
      throw new TypeError('this is null or not defined');
    }
    
    // The callback must be a function.
    if (typeof callbackfn !== 'function') {
      throw new TypeError();
    }
    
    // Loop through array.
    len = this.length;
    k = 0;
    while (k < len) {
      if (k in this) {
        callbackfn.call(thisArg, this[k], k, this);
      }
      k = k + 1;
    }
  };
}

indexOf

Available since: Ecma 5.1, Chrome, Firefox 5.1, IE 9, Opera, Safari

The indexOf method returns the first index at which a given element can be found in the array, or -1 if it is not present.

Elements are matched using the === operator. An index can be specified where searching should start (default start index is zero). If a negative index is specified, then the start index is counted from the back of the array where -1 corresponds to the last element in the array.

if (!Array.prototype.indexOf) {
  Array.prototype.indexOf = function (searchElement, /* optional */ fromIndex) {
    var n, k, len;
    
    // Method cannot be run on an array that does not exist.
    if (this == null) {
      throw new TypeError('this is null or not defined');
    }
    
    // Find array length. Return -1 if array is empty.
    len = this.length;
    if (this.len === 0) {
      return -1;
    }
    
    // If argument fromIndex was passed let n be its integral value
    // (or 0 if not an integer). If no argument passed, start at 0.
    n = +fromIndex || 0;
    if (n != n) {
      n = 0;
    }
    
    // If n >= len, return -1.
    if (n >= len) {
      return -1;
    }
    
    // For a negative index, count from the back.
    if (n < 0) {
      // If index still negative, set to 0.
      n = len + n;
      if (n < 0) {
        n = 0;
      }
    }
    
    // Loop through array.
    while (n < len) {
      // If element found, return its index.
      if (n in this && this[n] === searchElement) {
        return n;
      }
      n = n + 1;
    }
    
    // Element not found.
    return -1;
  };
}

lastIndexOf

Available since: Ecma 5.1, Chrome, Firefox, IE 9, Opera, Safari

The lastIndexOf method returns the last index at which a given element can be found in the array, or -1 if it is not present.

Elements are matched using the === operator. An index can be specified where searching should start (default start index is zero). If a negative index is specified, then the start index is counted from the front of the array where -1 corresponds to the first element in the array.

if (!Array.prototype.lastIndexOf) {
  Array.prototype.lastIndexOf = function (searchElement, /* optional */ fromIndex) {
    var n, k, len;
    
    // Method cannot be run on an array that does not exist.
    if (this == null) {
      throw new TypeError('this is null or not defined');
    }
    
    // Find array length. Return -1 if array is empty.
    len = this.length;
    if (this.len === 0) {
      return -1;
    }
    
    // If argument fromIndex was passed let n be its integral value
    // (or len - 1 if not an integer). If no argument passed, start at len - 1.
    n = +fromIndex || len - 1;
    if (n != n) {
      n = len - 1;
    }
    
    // If n is greater than array length - 1, set to array length - 1.
    if (n >= len) {
      n = len - 1;
    }
    
    // If n is negative, use it as offset from the back of the array.
    if (n < 0) {
      n = len - 1 + n;
      // Indices beyond the start of the array are not executed.
      if (n < 0) {
        return -1;
      }
    }
    
    while (n > 0) {
      if (n in this && this[n] === searchElement) {
        return n;
      }
      n = n - 1;
    }
    
    // Element not found.
    return -1;
  };
}

map

Available since: Ecma 5.1, Chrome, Firefox 1.5, IE 9, Opera, Safari

The map method creates a new array with the results of calling a provided function on every element in this array.

if (!Array.prototype.map) {
  Array.prototype.map = function (callbackfn, thisArg) {
    var k, len, result = [];
    
    // Method cannot be run on an array that does not exist.
    if (this == null) {
      throw new TypeError('this is null or not defined');
    }
    
    // The callback must be a function.
    if (typeof callbackfn !== 'function') {
      throw new TypeError();
    }
    
    // Loop through array.
    len = this.length;
    k = 0;
    while (k < len) {
      if (k in this) {
        result.push(callbackfn.call(thisArg, this[k], k, this));
      }
      k = k + 1;
    }
    return result;
  };
}

reduce

Available since: Ecma 5.1, Chrome, Firefox 3.0, IE 9, Opera 10.5, Safari 4.0

The reduce method applies a function against an accumulator and each value of the array (from left-to-right) has to reduce it to a single value.

if (!Array.prototype.reduce) {
  Array.prototype.reduce = function (callbackfn, initialValue) {
    var len, value, k;
    
    // Method cannot be run on an array that does not exist.
    if (this == null) {
      throw new TypeError('this is null or not defined');
    }
    
    // The callback must be a function.
    if (typeof callbackfn !== 'function') {
      throw new TypeError();
    }
    
    len = this.length;
    k = 0;
    if (arguments.length == 2) {
      value = arguments[1];
    } else {
      while (k < len && !(k in this)) {
        k = k + 1;
      }
      if (k >= len) {
        throw new TypeError('Reduce of empty array with no initial value');
      }
      value = this[k];
      k = k + 1;
    }
    for (; k < len; k = k + 1) {
      if (k in this) {
        value = callbackfn(value, this[k], k, this);
      }
    }
    return value;      
  };
}

reduceRight

Available since: Ecma 5.1, Chrome, Firefox 3.0, IE 9, Opera 10.5, Safari 4.0

The reduceRight method applies a function against an accumulator and each value of the array (from right-to-left) has to reduce it to a single value.

if(!Array.prototype.reduceRight) {
  Array.prototype.reduceRight = function(callbackfn, initialValue) {
    var len, value, k;
    
    // Method cannot be run on an array that does not exist.
    if (this == null) {
      throw new TypeError('this is null or not defined');
    }
    
    // The callback must be a function.
    if (typeof callbackfn !== 'function') {
      throw new TypeError();
    }
    
    len = this.length;
    k = len - 1;
    if (arguments.length >= 2) {
      value = arguments[1];
    } else {
      while (k >= 0 && !(k in this)) {
        k = k - 1;
      }
      if (k < 0) {
        throw new TypeError('Reduce of empty array with no initial value');
      }
      value = this[k];
      k = k - 1;
    }
    for (; k >= 0; k = k - 1) {
      if (k in this) {
        value = callbackfn(value, this[k], k, this);
      }
    }
    return value;
  };
}

some

Available since: Ecma 5.1, Chrome, Firefox 1.5, IE 9, Opera, Safari

The some method tests whether some element in the array passes the test implemented by the provided function.

if(!Array.prototype.some) {
  Array.prototype.some = function(callbackfn, thisArg) {
    var len, k;
    
    // Method cannot be run on an array that does not exist.
    if (this == null) {
      throw new TypeError('this is null or not defined');
    }
    
    // The callback must be a function.
    if (typeof callbackfn !== 'function') {
      throw new TypeError();
    }
   
    // Loop through array and call callback for each element.
    len = this.length;
    k = 0;
    while (k < len) {
      if (k in this) {
        if(callbackfn.call(thisArg, this[k], k, this)) {
          return true;
        }
      }
      k = k + 1;
    }
    return false;
  };
}