what is it and why are they needed?

Kirill Mylnikov

Frontend developer at Usetech

Hello everyone, I’m Kirill Mylnikov, frontend developer at Usetech.

Today I want to talk about JavaScript polyfills: what are they and why are they needed? In practice, we implement several polyfills: map, forEach, filter, reduce.

This article is suitable for beginners who are preparing for an interview, and experienced professionals. In the comments, you can talk about how you implement polyfills in your work.

So let’s start by defining a polyfill and then moving on to methods.

What is a polyfill?

Polyfill is code that implements some functionality that is not supported in some browsers. Implementing your own polyfill ensures that the functionality behaves consistently across different browsers.

As I wrote above, today we will implement several polyfills: map, forEach, filter, reduce.

map method

The map method calls a function on each element and returns a new array. The function argument takes three values:

  1. Array element;

  2. The index of the given element;

  3. the array itself.

Let’s implement a polyfill using an example:

Array.prototype.myMap = function (callback, arg) {
  if (this == null || this === window) {
    throw TypeError('myMap called on null or undefined')
  }
  if (typeof callback !== 'function') {
    throw TypeError(`{callback} is not a function`)
  }
  const newArray = [];
  for (let i = 0; i < this.length; i++) {
    newArray[i] = callback.call(arg, this[i], i, this)
  }
  return newArray;
}

Now let’s take a step-by-step look at what’s going on here. First we need to handle the resulting errors:

  1. The callback function may not be passed;

  2. This method is not called on an array.

this === null || this === windowthe condition will work if the method is called as a separate function.

Example:

const myMap = Array.prototype.myMap;

myMap();

Inside the myMap function, this will already be global, not in strict mode it will be window, in strict mode it will be undefined. For this case, we are throwing an error.

Also, the callback function may not be passed, and in this case we check for typeof callback === ‘function’

Our function takes a second argument arg. What is it for? If our callback function is to be called in a context, the inner callback must be set to arg. This can be done with call().

In such a simple way, we have implemented a polyfill of the map method. Now let’s move on to the next method.

forEach method

There are a few things to keep in mind when implementing the following forEach method polyfill:

  1. It is used only for iteration and does not return anything;

  2. Changes the original array.

The implementation of the polyfill will be very similar to the map method. Let’s look at an example:

Array.prototype.myForEach = function (callback, arg) {
  if (this == null || this === window)
    throw TypeError('myForEach called on null or undefined');
 
  if (typeof callback !== 'function')
    throw TypeError(`${callback} is not a function`);
 
  for (let i = 0; i < this.length; i++) {
    callback.call(arg, this[i], i, this);
  }
};

The checks are the same as the implementation, only we don’t return anything, we just iterate over.

filter method

This method returns a new array of all matching elements. Let’s see an example:

Array.prototype.myfilter = function (callback, arg) {
  if (this == null || this === window)
    throw TypeError('myfilter called on null or undefined');
 
  if (typeof callback !== 'function')
    throw TypeError(`${callback} is not a function`);
 
  const newArr = [];
  for (let i = 0; i < this.length; i++) {
     if (callback.call(arg, this[i], i, this)) newArr.push(this[i]);
  }
 
  return newArr;
};

As you can see, in this case there is error handling, like everyone else.

The only thing that has changed is that we have written a test that satisfies the condition before adding it to the array and returning it.

Let’s move on to the last method – reduce.

reduce method

Before we start parsing in practice, we need to remember how the reduce method works. Basically, it is used to calculate some single value based on the entire array. The function is applied in turn to all elements and transfers its result to the next call.

Function arguments:

  1. previousValue – the result of the previous call;

  2. item – array element;

  3. index – index of this element;

  4. array – the array itself.

Now let’s move on to the implementation of the reduce polyfill:

Array.prototype.myReduce = function (callback, initValue) {
  if (this === null || this === window)
    throw TypeError('myReduce called on null or undefined');
 
  if (typeof callback !== 'function')
    throw TypeError(`${callback} is not a function`);
 
  let previousValue = initValue;
  let startIndex = 0;
 
  if (initValue === null) {
    previousValue = this[0];
    startIndex = 1;
  }
 
  if (previousValue == null)
    throw TypeError('Reduce of empty array with no initial value');
 
  for (let index = startIndex; index < this.length; index++) {
    previousValue = callback(previousValue, this[index], index, this);
  }
 
  return previousValue;
};

I have already described the first two checks above. But a new check has appeared: if previousValue is undefined, if the array is empty and initialValue is not specified, then we also throw an error.

The second argument to reduce initialValue is optional and is used to initialize previousValue. If it is not specified, then we initialize the first element of the array, and start traversing from the second element.

We have analyzed several examples of polyfill implementation, as well as errors that occur and methods for fixing them. Thanks for taking the time to read the article. And in the comments, you can share your experience in creating and implementing polyfills.

Similar Posts

Leave a Reply