Перейти к содержанию

Вопросы на собеседовании JavaScript junior с ответами

Вопросы на собеседовании JavaScript junior полезно разбирать через поведение программы. Заученное определение заканчивается быстро; небольшой пример позволяет объяснить, почему получается именно такой результат и где это важно в интерфейсе. Разберём восемь вопросов на данных учебного каталога. Это подборка для подготовки, а не перечень обязательных вопросов всех работодателей.

Перед чтением ответа попробуйте предсказать вывод. Затем запустите пример и измените одно условие. Так вы проверяете понимание, а не способность узнать знакомый текст. Примеры языка ниже выполнены в Node.js 24.7.0. Сетевой пример отдельно проверяется по описанному маршруту; он зависит от адреса сервера и не использует настоящий сервис в комплекте упражнения.

1. Почему объект с const можно изменить?

const запрещает повторное присваивание самой переменной. Он не делает объект неизменяемым. В каталоге это легко увидеть на выбранном фильтре:

const selected = { topic: "all" };
selected.topic = "frontend";
console.log(selected.topic); // frontend

Переменная продолжает ссылаться на тот же объект, а его поле изменилось. Выражение selected = { topic: "backend" } было бы другим действием: попыткой присвоить новое значение переменной. Для такого переназначения нужен подходящий способ объявления, например let.

На собеседовании полезно проговорить оба ограничения. Фраза «const — это константа» без уточнения не объясняет наблюдаемое поведение. В реальном интерфейсе выбор между изменением объекта и созданием нового зависит ещё и от организации состояния приложения; это отдельное решение, а не свойство ключевого слова.

2. Чем строгое сравнение отличается от сравнения с приведением?

Значение поля формы обычно приходит строкой. Идентификатор материала в массиве может быть числом. Сравним два значения:

const inputId = "7";
const articleId = 7;
console.log(inputId === articleId); // false
console.log(inputId == articleId);  // true
console.log(Number(inputId) === articleId); // true

В последней строке преобразование видно явно. Для поиска по числовому идентификатору это удобнее скрытого согласования разных типов. Но Number сам по себе не проверяет правило поля: пустая строка преобразуется в ноль. Если допустимы только положительные целые идентификаторы, сначала определите эту границу и проверьте ввод.

Ответ должен связать оператор с задачей. Не требуется обещать, что строгое сравнение решает все ошибки. Оно помогает не смешивать типы незаметно; корректность введённого значения всё равно остаётся отдельным вопросом.

3. Когда использовать filter, а когда map?

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

const articles = [
  { title: "Grid", published: true },
  { title: "Forms", published: false },
  { title: "Arrays", published: true }
];
const titles = articles
  .filter(article => article.published)
  .map(article => article.title);
console.log(titles); // [ 'Grid', 'Arrays' ]

После фильтра остаются два объекта, после преобразования — две строки. Если поменять порядок и сначала оставить только названия, информация published будет потеряна. Для выбранной задачи порядок операций имеет смысл.

Оба вызова создают новые массивы, но callback может изменять переданные объекты. Поэтому утверждение «map всегда делает данные полностью независимыми» неверно. В нашем примере callback только читает поле. Описание операции и её параметров есть в документации MDN о map.

4. Что сохраняет замыкание?

Сделаем отдельный счётчик открытий для материала. Возвращённая функция использует переменную из своего внешнего окружения:

function makeOpenCounter() {
  let count = 0;
  return () => ++count;
}
const firstArticle = makeOpenCounter();
const secondArticle = makeOpenCounter();
console.log(firstArticle(), firstArticle(), secondArticle()); // 1 2 1

Каждый вызов makeOpenCounter создаёт своё окружение. Два счётчика поэтому независимы. У первого функция продолжает обращаться к его count, даже когда внешний вызов уже завершён. Такой доступ к окружающим переменным и составляет важную часть понимания замыкания. Основное понятие разобрано в руководстве MDN.

Для проверки замените две функции одной общей и вызовите её три раза. Вы получите последовательность одного счётчика. Более подробное развитие этого примера есть в уроке о замыканиях.

5. Почему копия через spread может менять исходный объект?

Spread создаёт поверхностную копию объекта. Вложенные объекты при этом не становятся автоматически независимыми:

const source = { title: "Grid", stats: { opens: 0 } };
const copy = { ...source };
copy.title = "New title";
copy.stats.opens = 3;
console.log(source.title, source.stats.opens); // Grid 3

Поле title в копии заменено отдельно, а stats у обоих объектов указывает на один вложенный объект. Если нужно независимо изменить именно статистику, создайте копию и этого уровня: { ...source, stats: { ...source.stats } }.

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

6. Что возвращает async-функция?

Результат async-функции представлен Promise, даже если внутри возвращается обычное число. Чтобы получить значение в асинхронном маршруте, его можно ожидать:

async function countPublished() {
  return 2;
}
const pending = countPublished();
console.log(pending instanceof Promise); // true
console.log(await pending); // 2

Этот пример запускается как ES-модуль, например файл с расширением .mjs. await здесь находится на верхнем уровне модуля. При переносе в обычный скрипт или другой контекст условия использования нужно учитывать отдельно.

Ожидание не означает, что JavaScript автоматически вынес вычисление в другой поток. Promise описывает результат операции и его завершение; тяжёлый синхронный цикл внутри функции всё ещё может блокировать выполнение. Сам механизм результата разобран в документации MDN о Promise и уроке серии JavaScript.

7. В каком порядке выполнятся сообщения?

Рассмотрим маленький фрагмент без таймеров и запросов:

console.log("start");
Promise.resolve().then(() => console.log("after promise"));
console.log("end");
// start
// end
// after promise

Обработчик then не выполняется между двумя синхронными сообщениями. Он будет вызван после завершения текущего синхронного кода. Для данного примера этого объяснения достаточно; не стоит переносить вывод на любой порядок событий сложного приложения без разбора остальных операций.

На собеседовании сначала назовите наблюдаемый порядок, затем причину. Если добавлен await, таймер или сетевой вызов, разберите новый фрагмент отдельно. Уверенное объяснение небольшой программы полезнее длинного списка терминов, который не помогает предсказать её вывод.

8. Почему try/catch не считает HTTP 404 успешного fetch ошибкой запроса?

Ответ сервера с HTTP 404 может быть получен нормально с точки зрения сетевого обмена. Поэтому прикладной код проверяет статус ответа, прежде чем читать полезные данные:

async function loadArticles(url) {
  const response = await fetch(url);
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}`);
  }
  return response.json();
}

Снаружи можно ожидать вызов в try/catch и показывать состояние ошибки. В нём окажутся и явно выброшенная ошибка статуса, и ошибка сети, и ошибка разбора некорректного JSON. Различать эти случаи для интерфейса может быть полезно; общий обработчик не делает их одной причиной. Это поведение поясняет MDN в руководстве по fetch.

Для воспроизведения подготовьте свой URL, который возвращает JSON с HTTP 200, затем вариант с HTTP 404. Посмотрите Network и текст ошибки. Эти сетевые ответы не входят в локальный комплект; не называйте их проверенными только после чтения функции.

Как готовиться по этой подборке

Выберите вопрос, который не смогли объяснить, и свяжите его с проектом. Для массивов измените фильтр каталога; для копирования добавьте независимое состояние; для Promise разберите загрузку. Запишите исходное ожидание, фактический вывод и объяснение расхождения.

После этого расскажите решение без чтения кода вслух по символам. Собеседнику важно понять выбранное поведение и ваши границы знания. Если тема пока незнакома, назовите это и предложите небольшой проверяемый шаг. Для систематического изучения языка вернитесь к серии «Современный JavaScript».

Скачать выполняемые примеры языка · Практикум по поиску работы