Интервью-вопрос
Что такое функция map
map используют, когда нужно преобразовать массив в новый массив значений. В ответе важно не спутать трансформацию с простым перебором и не забыть про мутацию объектов внутри callback.
- Добавлен
- Редакция
Подготовьте короткий ответ и пару деталей на случай уточняющих вопросов.
Мини-квиз
Проверка перед разбором
Несколько быстрых вопросов перед разбором. Так проще поймать места, которые только кажутся понятными.
Вопрос 1 из 50 правильно
Разбор
Разобраться, а не зазубрить
Дальше разбираем суть, типичные уточнения и места, где легко сказать лишнее или перепутать термины.
Базовая идея
map помогает ответить на вопрос: как из каждого элемента получить новый элемент. Он проходит по массиву, вызывает callback и складывает возвращенные значения в новый массив.
const numbers = [1, 2, 3];
const doubled = numbers.map((number) => number * 2);
console.log(doubled); // [2, 4, 6]
console.log(numbers); // [1, 2, 3]В коротком ответе важно сказать не только, что метод перебирает массив. Добавьте, что он возвращает новый массив. Иначе ответ звучит как описание forEach.
Контракт метода
У map простой контракт: сколько элементов было во входном массиве, столько будет и в результате. Каждый элемент результата равен тому, что вернул callback для элемента с тем же индексом.
Callback получает значение, индекс и сам массив:
const labels = ["html", "css", "js"].map((item, index) => {
return `${index + 1}. ${item.toUpperCase()}`;
});
// ["1. HTML", "2. CSS", "3. JS"]Если вы хотите убрать часть элементов, это уже не задача map. Используйте filter. Если хотите получить одно значение, например сумму, подумайте про reduce или обычный цикл.
Когда map выбран правильно
- 1Есть массив входных данных
- 2Для каждого элемента нужно получить новое значение
- 3Callback возвращает результат без лишних side effects
- 4Новый массив используется дальше в UI, API или вычислении
- 1map вызывается только ради console.log, push или запроса
- 2Результат метода игнорируется
- 3Callback мутирует объекты состояния
- 4Код выглядит как трансформация, но на деле прячет побочные эффекты
На интервью хорошо звучит, когда вы объясняете выбор метода через намерение. map читается как преобразование данных. Если в коде нет нового массива, намерение становится ложным.
Плохой пример:
users.map((user) => {
sendAnalytics(user.id);
});Здесь результат map не используется. Если такой код попадет в render или в effect с неверными зависимостями, он может отправлять лишние события аналитики при каждом обновлении UI. Лучше написать forEach внутри явного обработчика или вынести отправку аналитики в отдельную функцию, чтобы следующий разработчик не искал несуществующую трансформацию.
Мутации и React-состояние
Фраза про то, что map не меняет исходный массив, верная, но неполная. Метод создает новый массив, но элементы могут оставаться теми же ссылками. Если элемент - объект, его можно случайно изменить внутри callback.
Плохой пример:
const updated = users.map((user) => {
if (user.id === activeId) {
user.isActive = true;
}
return user;
});Такой код мутирует старый объект. Безопаснее вернуть новый объект только для измененного элемента:
const updated = users.map((user) => {
if (user.id !== activeId) {
return user;
}
return { ...user, isActive: true };
});Для frontend это не академическая деталь. Мутация состояния может сломать мемоизацию, оставить в UI старые данные, усложнить откат optimistic UI и сделать баги зависимыми от порядка рендеров.
map в JSX
Во frontend-разработке map часто встречается при рендере списков.
function UserList({ users }) {
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}Здесь важно упомянуть key. React использует его, чтобы сопоставлять элементы между рендерами. Если взять индекс как key в списке, который сортируется или меняется, фокус, введенный текст или локальное состояние дочерних компонентов может оказаться у другого элемента.
Асинхронный map
map не становится асинхронным из-за async callback. Он сразу вернет массив промисов.
const promises = ids.map(async (id) => {
const response = await fetch(`/api/users/${id}`);
return response.json();
});
const users = await Promise.all(promises);Это нормальный подход для параллельных независимых запросов. Но если запросов много, стоит подумать про лимит параллельности, отмену через AbortController, обработку ошибок и состояние загрузки. Иначе можно перегрузить API, получить race condition при смене параметров или оставить UI в непонятном состоянии после первой ошибки.
Практический вывод
Сильный ответ можно сформулировать так:
map - это метод массива для преобразования данных. Он вызывает callback на каждом элементе и возвращает новый массив результатов такой же длины. Сам массив он не меняет, но объекты внутри можно случайно мутировать по ссылке. Если мне нужен просто side effect, я выберу forEach или цикл. Если callback async, буду ждать массив промисов через Promise.all или другой подход.
Такая формулировка закрывает определение, отличие от похожих методов и практические риски, которые часто всплывают во frontend-коде.
Частые ошибки
Где обычно ошибаются
Проверьте формулировки, которые звучат уверенно, но на интервью быстро выдают пробелы.
- 1
Использовать map ради side effect
Если вы пишетеitems.map(sendAnalytics)и не используете результат, код вводит в заблуждение.mapобещает трансформацию данных, а не действие ради действия. Во frontend-коде это особенно опасно в render или плохо настроенном effect: можно отправить лишние события аналитики или повторить сетевой запрос. Для side effect лучше использоватьforEach, цикл или отдельную функцию с понятным названием. - 2
Забыть return в callback
В стрелочной функции с фигурными скобками нужно явно вернуть значение. Иначеmapсоздаст массив изundefined, и ошибка может всплыть уже в JSX или при отправке данных. Если тело callback короткое, можно использовать неявный возврат. - 3
Мутировать объекты внутри map
mapне меняет сам массив, но callback может изменить объект по ссылке. В React это особенно опасно, потому что состояние может измениться незаметно для логики обновления. Безопаснее возвращать новый объект через spread или явно описанную функцию преобразования. - 4
Ждать от async map готовые данные
array.map(async item => ...)возвращает массив промисов. Если передать его в рендер или обработчик как обычные данные, вы получите неправильное состояние или ошибку. Для ожидания всех операций используйтеawait Promise.all(promises)и отдельно решайте, что делать с ошибками. - 5
Использовать map вместо filter или reduce
mapсохраняет длину массива. Если нужно убрать элементы, используйтеfilter. Если нужно получить одно значение, например сумму или объект-словарь, обычно лучше подходитreduceили понятный цикл. - 6
Делать тяжелый map на каждом render
Преобразование большого массива перед JSX может выполняться при каждом render. Это дает лишнюю нагрузку на главный поток и заметные подвисания интерфейса. Безопаснее подготовить данные ближе к месту получения, разбить список, использовать виртуализацию или мемоизацию, если входные данные реально стабильны.
Follow-up
Что могут спросить дальше
Короткие ответы на вопросы, которыми интервьюер проверяет понимание map в JavaScript и frontend-коде.
Живые ответы
Видео с похожим вопросом
Если найдем публичные интервью с таким вопросом, добавим их сюда. Их удобно смотреть после теории, чтобы свериться с живыми ответами.
Пока видео нет. Когда появятся подходящие публичные интервью, добавим их в этот блок, чтобы можно было сравнить разбор с тем, как отвечают реальные кандидаты.
Что такое рекурсия 😎
Рекурсия это прием, при котором функция вызывает саму себя и обязательно имеет условие выхода. Разберем, как спокойно объяснить ее на интервью, где она полезна во фронтенде и чем опасна глубокая вложенность.
Что такое HashMap 😎
HashMap хранит пары ключ-значение и быстро находит значение по ключу через хэширование. Вы разберетесь, почему доступ считается O(1) в среднем, как мешают коллизии и почему ключи лучше делать неизменяемыми.