Array.prototype.lastIndexOf

JavaScript polyfill, by , Sunday, March 23rd, 2014

If you need the Array.prototype.lastIndexOf() method but an important part of your users use old browsers such as IE8, then you need either a library or a polyfill to ensure the expected behavior. I have written three polyfills of which you can choose as you feel fit. With that said, the last one listed is an 100% ECMAScript 5.1 compliant version, which might not be very practical to implement on a live site, but again it is up to you.

Array lastIndexOf polyfill with for-loop

This is properly the most straight forward way to write a polyfill for this method, even though you could as well have use a while-loop. But it is an easy rewrite, so I will only post the for-loop version here.

  1. if (!Array.prototype.lastIndexOf) {
  2. Array.prototype.lastIndexOf = function (elem, fIndex) {
  3. "use strict";
  4. var arr = this,
  5. frm = fIndex ? parseInt(fIndex) : arr.length - 1;
  6.  
  7. for (frm; frm >= 0; frm--) {
  8. if (arr.hasOwnProperty(frm) && arr[frm] === elem) {
  9. return frm;
  10. }
  11. }
  12. return -1;
  13. };
  14. }

Array lastIndexOf polyfill with recursion

Another approach to writing this polyfill is using recursion. In this case, I do not find it as elegant as the for-loop solution, but I still think it is a good practice to write and understand it.

  1. if (!Array.prototype.lastIndexOf) {
  2. Array.prototype.lastIndexOf = function (elem, fIndex) {
  3. "use strict";
  4. var arr = this,
  5. frm = fIndex ? parseInt(fIndex) : arr.length - 1;
  6. return (function findElm(i) {
  7. return (i >= 0) ? (function () {
  8. if (arr.hasOwnProperty(frm) && arr[frm] === elem) {
  9. return frm;
  10. }
  11. return findElm(frm--);
  12. }()) : -1;
  13. }(frm));
  14. };
  15. }

A 100% ECMAScript 5.1 compliant lastIndexOf polyfill

This last version of the polyfill is for those of you, who like me are interested in how the native implementation works. This approach follows the specifications, as defined in the ECMAScript 5.1 documentation, step by step, taking no shortcuts. Following the polyfill is a step by step guide on how it works.

  1. if (!Array.prototype.lastIndexOf) {
  2. Array.prototype.lastIndexOf = function (searchElement, fromIndex) {
  3. "use strict";
  4. var O = Object(this),
  5. lenValue = O.length,
  6. len = lenValue >>> 0,
  7. n,
  8. k,
  9. kPresent,
  10. elementK,
  11. same;
  12.  
  13. if (len === 0) {
  14. return -1;
  15. }
  16.  
  17. n = fromIndex ? parseInt(fromIndex) : len - 1;
  18.  
  19. k = (n >= 0) ? Math.min(n, len - 1) : len - Math.abs(n);
  20.  
  21. while (k >= 0) {
  22. kPresent = O.hasOwnProperty(k.toString());
  23. if (kPresent) {
  24. elementK = O[k.toString()];
  25. same = searchElement === elementK;
  26. if (same) {
  27. return k;
  28. }
  29. }
  30. k -= 1;
  31. }
  32. return -1;
  33. };
  34. }

The first three lines of this polyfill are not in the specifications but are necessary for us, as we need first to check if the functionality already exists, (line 1) then if it does not, declare it as a function that takes two arguments; searchElement and fromIndex, where the fromIndex is optional. The third line is forcing the function scope to be executed in strict mode. Now starting from line 4 this is what the specifications say;

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 "lenght".

  1. lenValue = O.length,

3. Let len be ToUint32(lenValue).

  1. len = lenValue >>> 0,

The lines from 7 to 11 are just declarations of variables used later on. We declare them at the top, because any declaration gets moved to the top of the current scope at execution anyway, and so to avoid any problems it is good practice to always declare them there in the first place.

4. If len is 0, return -1.

  1. if (len === 0) {
  2. return -1;
  3. }

5. If argument fromIndex was passed let n be ToInteger(fromIndex); else let n be len-1. (this is the step where we determine where to start the search for searchElement. If the argument fromIndex is set, we need to start from there, else we need to start at the end of the array, which is its length minus one, hence len-1.)

        n = fromIndex ? parseInt(fromIndex) : len - 1;

6. If n >= 0, then let k be min(n, len - 1).
7. Else, n < 0
7a. Let k be len - abs(n).
(I have done all three steps using the Conditional Operator ( ? : ) as it does not takeaway any readability or diverge from the spec.)

  1. k = (n >= 0) ? Math.min(n, len - 1) : len - Math.abs(n);

8. Repeat, while k>=0

  1. while (k >= 0) {

8a. Let kPresent be the result of calling the [[HasProperty]] internal method of O with argument ToString(k).

  1. kPresent = O.hasOwnProperty(k.toString());

8b. If kPresent is true, then

  1. if (kPresent) {

8b i. Let elementK be the result of calling the [[Get]] internal method of O with the argument ToString(k).

  1. elementK = O[k.toString()];

8b ii. Let same be the result of applying the Strict Equality Comparison Algorithm to searchElement and elementK. (The Strict Equality Comparison Algorithm is the one performed when you use triple equal. If you use double equal on the other hand, you will be performing the Abstract Equality Comparison Algorithm which does type coercion.)

  1. same = searchElement === elementK;

8b iii. If same is true, return k.

  1. if (same) {
  2. return k;
  3. }

8c. Decrease k by 1.

  1. k -= 1;

9. Return -1. (If you follow the code, you will see that this only is executed if the searchElement is not found in the array)

  1. return -1;

I hope this step by step guide on how the native implementation works, provides a better understanding on not just how the other polyfills work, but also on how to use this method properly in the first place.