Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Array Reduce Polyfill Explanation

Apologize in advance for lengthy post. I am trying to understand array reduce polyfill provided by MDN. I could not understand some lines in the polyfill can you explain it please. Below is the code

    if (!Array.prototype.reduce) {
      Object.defineProperty(Array.prototype, 'reduce', {
        value: function(callback /*, initialValue*/) {
          if (this === null) {
            throw new TypeError( 'Array.prototype.reduce ' + 
              'called on null or undefined' );
          }
          if (typeof callback !== 'function') {
            throw new TypeError( callback +
              ' is not a function');
          }

          // 1. Let O be ? ToObject(this value).
          var o = Object(this);

          // 2. Let len be ? ToLength(? Get(O, "length")).
          var len = o.length >>> 0; 

          // Steps 3, 4, 5, 6, 7      
          var k = 0; 
          var value;

          if (arguments.length >= 2) {
            value = arguments[1];
          } else {
            while (k < len && !(k in o)) {
              k++; 
            }

            // 3. If len is 0 and initialValue is not present,
            //    throw a TypeError exception.
            if (k >= len) {
              throw new TypeError( 'Reduce of empty array ' +
                'with no initial value' );
            }
            value = o[k++];
          }

          // 8. Repeat, while k < len
          while (k < len) {
            // a. Let Pk be ! ToString(k).
            // b. Let kPresent be ? HasProperty(O, Pk).
            // c. If kPresent is true, then
            //    i.  Let kValue be ? Get(O, Pk).
            //    ii. Let accumulator be ? Call(
            //          callbackfn, undefined,
            //          « accumulator, kValue, k, O »).
            if (k in o) {
              value = callback(value, o[k], k, o);
            }

            // d. Increase k by 1.      
            k++;
          }

          // 9. Return accumulator.
          return value;
        }
      });
    }

Question1: If you see step 1,

var o = Object(this);

I have checked both values of o and this, by passing an array to the polyfill method. There is no difference between o and this. They both are arrays(array.isarray returned true on both) with the same array value. Why not use below instead..?

var o = this;

Question2: Step 2

var len = o.length >>> 0;

Above line seems to right shift the o.length(32 bits). However no.of bits shifted is 0. So what advantage do we get by shifting 0 bits... Why not use below instead...?

var len = o.length;

Question 3: The first while condition inside else as below

 while (k < len && !(k in o)) {
    k++;
  }

Initially k is set to 0 and it always seems to exist in o. So this while loop condition never gets to be true. So why do we need this while loop, if it never gets inside.

like image 722
greg chap Avatar asked Dec 15 '17 21:12

greg chap


1 Answers

Question 1:

To make sure reduce is called on an object as reduce could be called through Function#call, Function#apply or even bound Function#bind:

Array.prototype.reduce.call(undefined, function() {});

So when accessing properties such as length, an error saying can't access property **** of undefined won't be thrown.

NOTE: the example above uses the native reduce which actually throws an error if not provided with an object.

Question 2:

To always have a valid integer value as length (even if it doesn't exist):

console.log(5 >>> 0);         // 5
console.log(5.5 >>> 0);       // 5
console.log("5" >>> 0);       // 5
console.log("hello" >>> 0);   // 0
console.log(undefined >>> 0); // 0

Question 3:

To deal with sparse arrays:

var arr = [5, 6];
arr[7000000] = 7;

arr.reduce(function(acc, v, i) {
  console.log("index:", i);
}, 0);

It won't go through all the indices from 0 to 7000000, only those that really exist.

like image 164
ibrahim mahrir Avatar answered Oct 23 '22 00:10

ibrahim mahrir