Кирилл Мыльников
Frontend разработчик в Usetech
Всем привет, я — Кирилл Мыльников, frontend разработчик компании Usetech.
Сегодня хочу рассказать о полифилах JavaScript: что это и зачем они нужны? На практике мы реализуем несколько полифилов: map, forEach, filter, reduce.
Эта статья подойдёт новичкам, которые готовятся к собеседованию, и опытным специалистам. В комментариях вы можете рассказать о том, как реализуете полифилы в своей работе.
Итак, начнём с определения полифила, а затем перейдём к методам.
Что такое полифил?
Полифил — это код, реализующий какую-то функциональность, которая не поддерживается в некоторых браузерах. Реализация собственного полифила обеспечивает единообразное поведение функциональности в разных браузерах.
Как я писал выше, сегодня мы будем реализовывать несколько полифилов: map, forEach, filter, reduce.
Метод map
Метод map вызывает функцию для каждого элемента и возвращает новый массив. Аргумент функции принимает три значения:
Элемент массива;
Индекс данного элемента;
Сам массив.
Реализуем полифил на примере:
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;
}
А теперь поэтапно разберём, что тут происходит. Сначала нам нужно обработать возникающие ошибки:
Функцию обратного вызова могут не передать;
Данный метод вызывается не для массива.
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 нужно учесть несколько моментов:
Он используется только для перебора и ничего не возвращает;
Изменяет оригинальный массив.
Реализация полифила будет очень похожа на метод 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. В основном его применяют для вычисления какого-нибудь единого значения на основе всего массива. Функция применяется по очереди ко всем элементам и переносит свой результат на следующий вызов.
Аргументы функции:
previousValue — результат предыдущего вызова;
item — элемент массива;
index — индекс данного элемента;
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. Если он не указан, то мы инициализируем первый элемент массива, и начинаем обход со второго элемента.
Мы разобрали несколько примеров реализации полифилов, а также возникающие ошибки и методы их исправления. Спасибо за то, что уделили время прочтению статьи. А в комментариях можете поделиться своим опытом создания и реализации полифилов.