> Как написать полифил для метода map в JavaScript (JavaScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: Aston

Стек: JavaScript

> Пример ответа

Короткий ответ

Полифил для map - это функция, повторяющая поведение нативного метода Array.prototype.map. Она принимает callback и опциональный thisArg, вызывает callback для каждого элемента массива в порядке возрастания индексов и возвращает новый массив с результатами. Ключевые моменты: пропуск "дырок" в разреженных массивах, корректная работа с thisArg, передача трёх аргументов в callback (элемент, индекс, массив) и неизменность исходного массива.

Подробное объяснение

Array.prototype.map - это метод, который создаёт новый массив, заполняя его результатами вызова предоставленной функции для каждого элемента исходного массива. Полифил нужен для поддержки старых браузеров (например, IE8 и ниже), где этот метод отсутствует.

Спецификация ECMAScript определяет алгоритм Array.prototype.map следующим образом:

  1. Преобразовать this к объекту (ToObject).
  2. Получить значение свойства length (привести к ToLength).
  3. Проверить, что callback - функция (IsCallable), иначе выбросить TypeError.
  4. Создать новый массив с длиной, равной длине исходного (ArraySpeciesCreate).
  5. Пройти по индексам от 0 до length - 1:
    • Если индекс существует в массиве (HasProperty), вызвать callback с тремя аргументами: элемент, индекс, массив.
    • Записать результат в новый массив по тому же индексу.
    • Если индекс отсутствует (дырка в разреженном массиве), пропустить его - в новом массиве тоже будет дырка.
  6. Вернуть новый массив.

Важные нюансы реализации:

  • Разреженные массивы: нативный map не вызывает callback для "дырок" (например, [1, , 3]). Полифил должен проверять наличие свойства через in или hasOwnProperty, а не просто обращаться по индексу.
  • thisArg: если передан второй аргумент, он становится this внутри callback. Если не передан - undefined (в строгом режиме) или глобальный объект (в нестрогом).
  • Длина массива фиксируется до начала итерации: если callback изменяет массив, количество итераций не меняется, но значения могут быть другими.
  • Создание нового массива: исходный массив не мутируется. Новый массив создаётся через конструктор, но в полифиле достаточно new Array(length).

На практике

Полифил обычно добавляют в начало скрипта или в отдельный файл, подключаемый условно. В современных проектах используют транспиляторы (Babel) или полифилы из core-js, но понимание ручной реализации полезно для собеседований и отладки.

Практические сценарии использования полифила:

  • Поддержка старых браузеров без нативного map.
  • Кастомная версия map с дополнительной логикой (например, асинхронная обработка).
  • Образовательные цели - демонстрация внутреннего устройства метода.

Пример кода

JAVASCRIPT
if (!Array.prototype.map) {
Array.prototype.map = function(callback, thisArg) {
// 1. Преобразуем this к объекту
var array = Object(this);
// 2. Получаем длину
var length = array.length >>> 0;
// 3. Проверяем callback
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
// 4. Создаём новый массив
var result = new Array(length);
// 5. Итерируем
for (var i = 0; i < length; i++) {
// Проверяем, существует ли индекс (пропускаем дырки)
if (i in array) {
result[i] = callback.call(thisArg, array[i], i, array);
}
}
// 6. Возвращаем результат
return result;
};
}

Пример использования:

JAVASCRIPT
var numbers = [1, 2, 3];
var doubled = numbers.map(function(num) {
return num * 2;
});
console.log(doubled); // [2, 4, 6]
// Разреженный массив
var sparse = [1, , 3];
var result = sparse.map(function(x) { return x * 10; });
console.log(result); // [10, , 30] - дырка сохраняется

Как отвечать на собеседовании

Начните с краткого определения: "Полифил - это реализация метода, повторяющая поведение нативного map". Затем перечислите ключевые шаги алгоритма: преобразование this, проверка callback, создание нового массива, итерация с проверкой существования индекса. Обязательно упомяните обработку разреженных массивов и thisArg.

Покажите код и объясните каждую строку. Если спросят про edge cases, расскажите про:

  • length >>> 0 - приведение к беззнаковому 32-битному целому.
  • i in array - проверка наличия свойства, а не значения.
  • callback.call(thisArg, ...) - передача контекста.

Завершите примером использования и упомяните, что в реальной разработке лучше использовать core-js, но ручная реализация помогает понять спецификацию.

Что проверяет интервьюер

  • Понимание спецификации ECMAScript и алгоритма map.
  • Внимание к деталям: разреженные массивы, thisArg, приведение типов.
  • Умение писать чистый, безопасный код без побочных эффектов.
  • Знание различий между in, hasOwnProperty и простым обращением по индексу.
  • Понимание, зачем нужны полифилы и как они встраиваются в проект.

Типичные ошибки

  • Отсутствие проверки callback: если передать не функцию, нативный метод выбрасывает TypeError, а упрощённый полифил - нет.
  • Игнорирование разреженных массивов: обращение array[i] для дырки вернёт undefined, и callback будет вызван, что неверно.
  • Использование for...in: он перечисляет и унаследованные свойства, что ломает логику.
  • Мутация исходного массива: полифил должен возвращать новый массив, а не изменять this.
  • Забытый thisArg: если не использовать callback.call(thisArg, ...), контекст будет потерян.
  • Неправильное приведение length: если не использовать >>> 0, строка или объект с length могут сломать итерацию.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью