Gernar
Frontend DeveloperJavaScript

Интервью-вопрос

Что такое функция map

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

Добавлен
Редакция

Подготовьте короткий ответ и пару деталей на случай уточняющих вопросов.

🐰0
🥚0

Мини-квиз

Проверка перед разбором

Несколько быстрых вопросов перед разбором. Так проще поймать места, которые только кажутся понятными.

Вопрос 1 из 50 правильно

Как лучше коротко объяснить map на интервью?

Вы хотите дать точное определение без лишней теории.

Варианты ответа

Разбор

Разобраться, а не зазубрить

Дальше разбираем суть, типичные уточнения и места, где легко сказать лишнее или перепутать термины.

Базовая идея

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 выбран правильно

Хорошее применение map
  1. 1Есть массив входных данных
  2. 2Для каждого элемента нужно получить новое значение
  3. 3Callback возвращает результат без лишних side effects
  4. 4Новый массив используется дальше в UI, API или вычислении
Опасное применение map
  1. 1map вызывается только ради console.log, push или запроса
  2. 2Результат метода игнорируется
  3. 3Callback мутирует объекты состояния
  4. 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. 1

    Использовать map ради side effect

    Если вы пишете items.map(sendAnalytics) и не используете результат, код вводит в заблуждение. map обещает трансформацию данных, а не действие ради действия. Во frontend-коде это особенно опасно в render или плохо настроенном effect: можно отправить лишние события аналитики или повторить сетевой запрос. Для side effect лучше использовать forEach, цикл или отдельную функцию с понятным названием.
  2. 2

    Забыть return в callback

    В стрелочной функции с фигурными скобками нужно явно вернуть значение. Иначе map создаст массив из undefined, и ошибка может всплыть уже в JSX или при отправке данных. Если тело callback короткое, можно использовать неявный возврат.
  3. 3

    Мутировать объекты внутри map

    map не меняет сам массив, но callback может изменить объект по ссылке. В React это особенно опасно, потому что состояние может измениться незаметно для логики обновления. Безопаснее возвращать новый объект через spread или явно описанную функцию преобразования.
  4. 4

    Ждать от async map готовые данные

    array.map(async item => ...) возвращает массив промисов. Если передать его в рендер или обработчик как обычные данные, вы получите неправильное состояние или ошибку. Для ожидания всех операций используйте await Promise.all(promises) и отдельно решайте, что делать с ошибками.
  5. 5

    Использовать map вместо filter или reduce

    map сохраняет длину массива. Если нужно убрать элементы, используйте filter. Если нужно получить одно значение, например сумму или объект-словарь, обычно лучше подходит reduce или понятный цикл.
  6. 6

    Делать тяжелый map на каждом render

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

Follow-up

Что могут спросить дальше

Короткие ответы на вопросы, которыми интервьюер проверяет понимание map в JavaScript и frontend-коде.

Живые ответы

Видео с похожим вопросом

Если найдем публичные интервью с таким вопросом, добавим их сюда. Их удобно смотреть после теории, чтобы свериться с живыми ответами.

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

Содержание