Array.prototype.filter

JavaScript polyfill, by , Tuesday, March 11th, 2014

The Array.prototype.filter() method is not supported in IE8 and lower, so if an important part of your visitors uses older IE browsers, here is a polyfill for it;

  1. if (!Array.prototype.filter) {
  2. Array.prototype.filter = function (fn, context) {
  3. var i,
  4. value,
  5. result = [],
  6. length = this.length;
  7. for (i = 0; i < length; i++) {
  8. if (this.hasOwnProperty(i)) {
  9. value = this[i];
  10. if (fn.call(context, value, i, this)) {
  11. result.push(value);
  12. }
  13. }
  14. }
  15. return result;
  16. };
  17. }

The above polyfill is most likely what you would want to use on your website, but beyond what is practical and usable is what the documentation actually says. So if you are curious about that, and what lies behind the native implementation of the filter method, here is a 100% ECMAScript 5.1 compliant polyfill, followed by a walk through of each step;

  1. if (!Array.prototype.filter) {
  2. Array.prototype.filter = function(callbackfn , thisArg) {
  3. "use strict";
  4. var O = Object(this),
  5. lenValue = O.length,
  6. len = lenValue >>> 0,
  7. T,
  8. A,
  9. k,
  10. to,
  11. Pk,
  12. kPresent,
  13. kValue,
  14. selected;
  15.  
  16. if (typeof callbackfn !== "function") {
  17. throw new TypeError();
  18. }
  19.  
  20. T = thisArg ? thisArg : undefined;
  21. A = new Array();
  22. k = 0;
  23. to = 0;
  24. while(k < len) {
  25. Pk = k.toString();
  26. kPresent = O.hasOwnProperty(Pk);
  27. if (kPresent) {
  28. kValue = O[Pk];
  29. selected = callbackfn.call(T, kValue, k, O);
  30. if (!!selected) {
  31. A.push(kValue);
  32. to += 1;
  33. }
  34. }
  35. k += 1;
  36. }
  37. return A;
  38. };
  39. }

Now lets have a look at it line by line, following the ECMA-262 documentation;

  1. if (!Array.prototype.filter) {
  2. Array.prototype.filter = function(callbackfn , thisArg) {
  3. "use strict";

These first three lines are not in the standard, but we need them to first check if the Array.prototype.filter method is available, then if it is not, declare and initialize it to be a function that takes two arguments; callbackfn and thisArg. Here the callbackfn is, according to the ECMAScript 5.1 standard;

... a function that accepts three arguments and returns a value that is coercible to the Boolean value true or false.

The argument thisArg is the scope in which to call the callbackfn function, i.e. the value to use as this. The third line is forcing the code inside the filter method scope to be executed in strict mode, which is a more clean and safe subset of JavaScript. From now on, we are following the steps specified in the documentation;

1. Let O be the result of calling ToObject passing the this value as the argument.

  1. var O = Object(this),

2. Let lenValue be the result of calling the [[Get]] internal method of O with the argument "length".

  1. lenValue = O.length,

3. Let len be ToUint32(lenValue).

  1. len = lenValue >>> 0,

4. If IsCallable(callbackfn) is false, throw a TypeError exception.

  1. if (typeof callbackfn !== "function") {
  2. throw new TypeError();
  3. }

5. If thisArg was supplied, let T be thisArg; else let T be undefined.

  1. T = thisArg ? thisArg : undefined;

6. Let A be a new array created as if by the expression new Array() where Array is the standard build-in constructor with that name. (it is discouraged to declare an array using the Array constructor. Instead you should use literal notation like so; A = []; but as it is specifically stated in the documentation that the build in Array constructor should be used, that is what we will do)

  1. A = new Array();

7. Let k be 0.
8. Let to be 0.

  1. k = 0;
  2. to = 0;

9. Repeat, while k < len

  1. while(k < len) {

9a. Let Pk be ToString(k).

  1. Pk = k.toString();

9b. Let kPresent be the result of calling the [[HasProperty]] internal method of O with argument Pk.

  1. kPresent = O.hasOwnProperty(Pk);

9c. If kPresent is true, then

  1. if (kPresent) {

9c i. Let kValue be te result of clling te [[Get]] internal method of O with argument Pk

  1. kValue = O[Pk];

9c ii. Let selected be the result of calling the [[Call]] internal method of callbackfn wit T as the this value and argument list containing kValue, k, and O.

  1. selected = callbackfn.call(T, kValue, k, O);

9c iii. If ToBoolean(selected) is true, then

  1. if (!!selected) {

9c iii 1. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(to), Property Descriptor {[[Value]]: kValue, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. (here we are using the Array.prototype.push method, but technically we should be using the Object.defineProperty, but it is rather new and cumbersome to type out, and not that widely supported in browsers yet.)

  1. A.push(kValue);

9c iii 2. Increase to by 1.

  1. to += 1;

9d. Increase k by 1.

  1. k += 1;

10. Return A

  1. return A;

As you can see, writing according to documentation at the frontend, takes a lot of coding, and to make sure you have the filter method available, you can safely use the first simple polyfill. The documentation cited here is from the ECMA-262 standard, page 149 bottom to 150 top.