Количество просмотров186
12 сентября 2022

Полифилы JavaScript: что это и зачем они нужны?

Всем привет, я — Кирилл Мыльников, frontend разработчик компании Usetech.

Сегодня хочу рассказать о полифилах JavaScript: что это и зачем они нужны? На практике мы реализуем несколько полифилов: map, forEach, filter, reduce. 

Эта статья подойдёт новичкам, которые готовятся к собеседованию, и опытным специалистам. В комментариях вы можете рассказать о том, как реализуете полифилы в своей работе.

Итак, начнём с определения полифила, а затем перейдём к методам.

Что такое полифил?

Что такое Полифил — это код, реализующий какую-то функциональность, которая не поддерживается в некоторых браузерах. Реализация собственного полифила обеспечивает единообразное поведение функциональности в разных браузерах.

Как я писал выше, сегодня мы будем реализовывать несколько полифилов: map, forEach, filter, reduce.

Метод map

Метод map вызывает функцию для каждого элемента и возвращает новый массив.  Аргумент функции принимает три значения:

1. Элемент массива;

2. Индекс данного элемента;

3. Сам массив.

Реализуем полифил на примере:

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;
}

А теперь поэтапно разберём, что тут происходит. Сначала нам нужно обработать возникающие ошибки:

  1.  Функцию обратного вызова могут не передать;

  2.  Данный метод вызывается не для массива.

this === null || this === window, условие сработает в том случае, если метод вызывается как отдельная функция.

Пример:

const myMap = Array.prototype.myMap;

myMap();

Внутри функции myMap this уже будет как global, не в строгом режиме будет window, в строгом undefined. Для этого кейса мы выкидываем ошибку.

Также функцию обратного вызова могут не передать, и на этот случай мы делаем проверку на typeof callback === ‘function’

Наша функция принимает второй аргумент arg. Для чего это нужно? Если наша функция обратного вызова должна быть вызвана в контексте, для внутреннего callback должно быть установлено значение arg. Это можно сделать с помощью call().

Вот таким простым способом мы с вами реализовали полифил метода map.

Метод forEach

При реализации следующего полифила метода forEach нужно учесть несколько моментов:

  1. Он используется только для перебора и ничего не возвращает;
  2. Изменяет оригинальный массив.

Реализация полифила будет очень похожа на метод map. Посмотрим на примере:

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);
  }
};

Проверки остались такими же, как и реализация, только мы ничего не возвращаем, а просто перебираем.

Метод filter

Этот метод возвращает новый массив всех подходящих элементов. Посмотрим пример:

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;
};

Как вы видите, в этом случае присутствует обработка ошибок, как и у всех. 

Единственное, что поменялось, это то, что мы написали проверку, удовлетворяющую условию, перед тем как добавить в массив и вернуть его.

Перейдём к последнему методу — reduce.

Метод reduce

Прежде чем приступить к разбору на практике, нужно вспомнить, как работает метод reduce. В основном его применяют для вычисления какого-нибудь единого значения на основе всего массива. Функция применяется по очереди ко всем элементам и переносит свой результат на следующий вызов.

Аргументы функции:

  1. previousValue — результат предыдущего вызова;
  2. item — элемент массива;
  3. index — индекс данного элемента;
  4. array — сам массив.

Теперь перейдём к реализации полифила reduce:

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;
};

Первые две проверки я уже описывал выше. Но появилась новая проверка: если previousValue будет undefined, если массив пуст и не указан initialValue, то тоже выдаём ошибку. 

Второй аргумент reduce initialValue необязательный, и он используется для инициализации previousValue. Если он не указан, то мы инициализируем первый элемент массива, и начинаем обход со второго элемента.

Мы разобрали несколько примеров реализации полифилов, а также возникающие ошибки и методы их исправления. Спасибо за то, что уделили время прочтению статьи. А в комментариях можете поделиться своим опытом создания и реализации полифилов.