Mastering JavaScript Polyfills: A Comprehensive Overview

Vineet RajVineet Raj
8 min read

Have you ever wondered ? like while writing code for website how does browser know about the .map() .filter() etc

Well it’s because of prototype described in all of the browser but what if someone is using a browser that does not supports any of those like it is returning Error arr.map() is not a function

What will you do ? well I will try to write my own .map function !

So as we already discussed about what is poly fill and prototype let’s go and write our own polyfills


Steps to write Polyfill

  • Understand what you have to create : I mean if don’t know what map function does then how can you write alternative for it . so first of all study about the method

  • start creating it and try to use old javascript : you know browser is old and not supporting .map() may also be not supporting .push() so let’s use simple for loop for creating our polyfill


Array.push() | Array.myPush()

Characteristic of .push() method

  • It takes an element as argument

  • appends the array for which it is called with the provided value

  • overrides original array

  • returns updated length

  • time complexity : O(1)

if (!Array.prototype.myPush) {
  Array.prototype.myPush = function (val) {
    this[this.length] = val;
    return this.length;
  };
}

Array.pop() | Array.myPop()

Characteristic of .pop() method

  • It does not takes any argument

  • removes last element from array

  • overrides original array

  • returns element which is being removed

  • time complexity : O(1)

if (!Array.prototype.myPop) {
  Array.prototype.myPop = function () {
    let lastVal = this[this.length - 1];
    this.length -= 1;
    return lastVal;
  };
}

Array.unshift() | Array.myUnshift()

Characteristic of .unshift() method

  • It takes on element as argument

  • It increases arr size → shift all elements to right by 1 space

  • places argument val at 0th index

  • overrides original array

  • returns updated array size

  • time complexity : O(n)

if (!Array.prototype.myUnshift) {
  Array.prototype.myUnshift = function (val) {
    let newSize = this.length + 1;
    for (let i = newSize - 1; i > 0; i--) {
      this[i] = this[i - 1];
    }
    this[0] = val;
    return this.length;
  };
}

Array.shift() | Array.myShift()

Characteristic of .shift() method

  • does not takes any argument

  • starting from index 1 shifts all elements to left

  • decreases arr length

  • overrides original array

  • returns removed elements

  • time complexity : O(n)

if (!Array.prototype.myShift) {
  Array.prototype.myShift = function () {
    let firstElem = this[0];

    for (let i = 0; i < this.length - 1; i++) {
      this[i] = this[i + 1];
    }
    this.length--;

    return firstElem;
  };
}

Array.map() | Array.myMap()

Characteristic of .map() method

  • Takes a callback function as argument

    • Callback function must return a value
  • starting from index 0 runs callback function on all elements

  • does not overrides original array

  • returns new array after applying the function

  • time complexity : O(n)

if (!Array.prototype.myMap) {
  Array.prototype.myMap = function (userFn) {
    if (typeof userFn !== "function") {
      throw new Error(
        `For myFilter got ${typeof userFn} but Expected a Function as argument`
      );
    }
    const newArray = new Array(this.length); // Preallocate space

    for (let i = 0; i < this.length; i++) {
      newArray[i] = userFn(this[i], i);
    }

    return newArray;
  };
}

Array.filter() | Array.myFilter()

Characteristic of .filter() method

  • Takes a callback function as argument

    • callback function must return a value
  • starting from index 0 runs callback function on all elements

    • for which callback function returns true
  • valid values are then pushed into empty array

  • does not overrides original array

  • returns new array after applying the function

  • time complexity : O(n)


if (!Array.prototype.myFilter) {
  Array.prototype.myFilter = function (userFn) {
    if (typeof userFn !== "function") {
      throw new Error(
        `For myFilter got ${typeof userFn} but Expected a Function as argument`
      );
    }

    let newArr = [];

    for (let i = 0; i < this.length; i++) {
      if (userFn(this[i], i)) {
        newArr.myPush(this[i]);
      }
    }

    return newArr;
  };
}

Array.some() | Array.mySome()

Characteristic of .some() method

  • Takes a callback function as argument

    • callback function must return a value
  • starting from index 0 runs callback function on all elements

    • Finds a element for which callback function returns true
  • true is returned when function returns true even for one element

    • else false is returned
  • does not overrides original array

  • time complexity : O(n)


if (!Array.prototype.mySome) {
  Array.prototype.mySome = function (userFunc) {
    if (typeof userFunc !== "function") {
      throw new Error(`Received ${typeof userFunc} ,expected 'function`);
    }
    for (let i = 0; i < this.length; i++) {
      if (userFunc(this[i], i)) return true;
    }
    return false;
  };
}

Array.reduce() | Array.myReduce()

Characteristic of .reduce() method

  • Takes a callback function as argument along with an initial value

  • starting from index 0 runs callback function on all elements

    • applies callback function and then update the accumulated value
  • returns final accumulated value

  • does not overrides original array

  • time complexity : O(n)

if (!Array.prototype.myReduce) {
  Array.prototype.myReduce = function (userFunc, initialVal = 0) {
    if (typeof userFunc !== "function") {
      throw new Error(`Received ${typeof userFunc} ,expected 'function`);
    }
    let accumulatedResult = initialVal;
    for (let i = 0; i < this.length; i++) {
      accumulatedResult = userFunc(accumulatedResult, this[i], i);
    }

    return accumulatedResult;
  };
}

Array.every() | Array.myEvery()

Characteristic of .every() method

  • Takes a callback function as argument

    • callback function must return a value
  • starting from index 0 runs callback function on all elements

    • Finds a element for which callback function returns false
  • false is returned when function returns false even for one element

    • else true is returned
  • does not overrides original array

  • time complexity : O(n)

if (!Array.prototype.myEvery) {
  Array.prototype.myEvery = function (userFunc) {
    if (typeof userFunc !== "function") {
      throw new Error(`Received ${typeof userFunc} ,expected 'function`);
    }
    for (let i = 0; i < this.length; i++) {
      if (!userFunc(this[i], i)) {
        return false;
      }
    }

    return true;
  };
}

Array.find() | Array.myFind()

Characteristic of .find() method

  • Takes a callback function as argument

    • callback function must return a value
  • starting from index 0 runs callback function on all elements

    • Finds a element for which callback function returns true
  • value of first element is returned for which callback function returned true

    • else if no element found undefined is returned
  • does not overrides original array

  • time complexity : O(n)

if (!Array.prototype.myFind) {
  Array.prototype.myFind = function (userFunc) {
    if (typeof userFunc !== "function") {
      throw new Error(`Received ${typeof userFunc} ,expected 'function`);
    }
    for (let i = 0; i < this.length; i++) {
      if (userFunc(this[i], i)) {
        return this[i];
      }
    }
    return undefined;
  };
}

Array.findIndex() | Array.myFindIndex()

Characteristic of .findIndex() method

  • Takes a callback function as argument

    • callback function must return a value
  • starting from index 0 runs callback function on all elements

    • Finds a element for which callback function returns true
  • index of first element is returned for which callback function returned true

    • else if no element found -1 is returned
  • does not overrides original array

  • time complexity : O(n)


if (!Array.prototype.myFindIndex) {
  Array.prototype.myFindIndex = function (userFunc) {
    if (typeof userFunc !== "function") {
      throw new Error(`Received ${typeof userFunc} ,expected 'function`);
    }
    for (let i = 0; i < this.length; i++) {
      if (userFunc(this[i], i)) {
        return i;
      }
    }

    return undefined;
  };
}

Array.fill() | Array.myFill()

Characteristic of .fill() method

  • Takes a variable* as argument also may take start and end indexes

  • starting from index start runs for loop till end index

    • replaces all the elements between this range with value of variable passed
  • updated arr is returned

  • does not overrides original array

  • time complexity : O(n)

if (!Array.prototype.myFill) {
  Array.prototype.myFill = function (val, start = 0, end = this.length - 1) {
    let newArr = JSON.parse(JSON.stringify(this));

    for (let i = start; i <= end; i++) {
      newArr[i] = val;
    }

    return newArr;
  };
}

Array.splice() | Array.mySplice()

Characteristic of .splice() method

  • Takes a start index as argument also may take delete count and …items to be inserted

  • from index 0 to startIndex-1 all elements are kept as it is

  • elements from start index are deleted till delete count full fills

  • now …items are inserted at start index

    • after inserting all …items other elements of main array is kept as it is at end of updated array
  • overrides original array

  • time complexity : O(n)


if (!Array.prototype.mySplice) {
  Array.prototype.mySplice = function (
    startIndex,
    deleteCnt = this.length,
    ...items
  ) {
    let newArr = [];

    for (let i = 0; i < startIndex; i++) {
      newArr.push(this[i]);
    }

    newArr.push(...items);

    for (let i = startIndex + deleteCnt; i < this.length; i++) {
      newArr.push(this[i]);
    }
    this.length = 0;
    for (let i = 0; i < newArr.length; i++) {
      this.push(newArr[i]);
    }
  };
}

Hooray ! just wrote multiple polyfills for various array prototype so now you are in better place and you can write similar polyfills for object,string or any other datatype

  • if there is a predefined method

  • you can write it’s polyfill to make sure that you website runs on every platform

Just remember

  • Analyse Characteristic of method for which going to write polyfill

  • now write the polyfill

6
Subscribe to my newsletter

Read articles from Vineet Raj directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Vineet Raj
Vineet Raj