> Как написать полифил для метода map в JavaScript (JavaScript)
Уровень: senior · Роль: frontend · Категория: Технические вопросы
Компании: Aston
Стек: JavaScript
> Пример ответа
Короткий ответ
Полифил для map - это функция, повторяющая поведение нативного метода Array.prototype.map. Она принимает callback и опциональный thisArg, вызывает callback для каждого элемента массива в порядке возрастания индексов и возвращает новый массив с результатами. Ключевые моменты: пропуск "дырок" в разреженных массивах, корректная работа с thisArg, передача трёх аргументов в callback (элемент, индекс, массив) и неизменность исходного массива.
Подробное объяснение
Array.prototype.map - это метод, который создаёт новый массив, заполняя его результатами вызова предоставленной функции для каждого элемента исходного массива. Полифил нужен для поддержки старых браузеров (например, IE8 и ниже), где этот метод отсутствует.
Спецификация ECMAScript определяет алгоритм Array.prototype.map следующим образом:
- Преобразовать
thisк объекту (ToObject). - Получить значение свойства
length(привести кToLength). - Проверить, что
callback- функция (IsCallable), иначе выброситьTypeError. - Создать новый массив с длиной, равной длине исходного (
ArraySpeciesCreate). - Пройти по индексам от 0 до
length - 1:- Если индекс существует в массиве (
HasProperty), вызвать callback с тремя аргументами: элемент, индекс, массив. - Записать результат в новый массив по тому же индексу.
- Если индекс отсутствует (дырка в разреженном массиве), пропустить его - в новом массиве тоже будет дырка.
- Если индекс существует в массиве (
- Вернуть новый массив.
Важные нюансы реализации:
- Разреженные массивы: нативный
mapне вызывает callback для "дырок" (например,[1, , 3]). Полифил должен проверять наличие свойства черезinилиhasOwnProperty, а не просто обращаться по индексу. thisArg: если передан второй аргумент, он становитсяthisвнутри callback. Если не передан -undefined(в строгом режиме) или глобальный объект (в нестрогом).- Длина массива фиксируется до начала итерации: если callback изменяет массив, количество итераций не меняется, но значения могут быть другими.
- Создание нового массива: исходный массив не мутируется. Новый массив создаётся через конструктор, но в полифиле достаточно
new Array(length).
На практике
Полифил обычно добавляют в начало скрипта или в отдельный файл, подключаемый условно. В современных проектах используют транспиляторы (Babel) или полифилы из core-js, но понимание ручной реализации полезно для собеседований и отладки.
Практические сценарии использования полифила:
- Поддержка старых браузеров без нативного
map. - Кастомная версия
mapс дополнительной логикой (например, асинхронная обработка). - Образовательные цели - демонстрация внутреннего устройства метода.
Пример кода
JAVASCRIPTif (!Array.prototype.map) {Array.prototype.map = function(callback, thisArg) {// 1. Преобразуем this к объектуvar array = Object(this);// 2. Получаем длинуvar length = array.length >>> 0;// 3. Проверяем callbackif (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;};}
Пример использования:
JAVASCRIPTvar 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могут сломать итерацию.
> Похожие задачи по frontend
Как работает массив tickets в JavaScript
Почему Promise.race не подходит для выбора лучшего покупателя в JavaScript
Как исправить проблему с var в цикле с setTimeout в JavaScript
Что происходит при рекурсивном вызове с созданием промисов в JavaScript: зависнет ли страница или будет пропускаться рендер
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью