Собеседование по JavaScript: вопросы и мок-интервью
Страница для фронтенд- и фулстек-разработчиков, которые знают JavaScript, но хотят уверенно объяснять его на собеседовании. Выберите уровень или загрузите резюме либо вакансию, ответьте голосом на вопросы (до 50) и получите отчёт с оценкой технической точности, полноты и ясности. Ниже — реальные вопросы на собеседовании JavaScript и задачи на понимание кода: от областей видимости и приведения типов до event loop, утечек памяти и асинхронной архитектуры.
О чём спрашивает ИИ-интервьюер
Области видимости и замыкания
var против let/const, блочная область видимости, hoisting и temporal dead zone, как замыкание удерживает переменные. Классика — setTimeout в цикле.
this, прототипы и классы
Как место вызова определяет this, чем отличаются стрелочные функции и как синтаксис class ложится на цепочку прототипов.
Event loop и микрозадачи
Стек вызовов, очередь задач и очередь микрозадач, где в этом цикле рендеринг. Задачи «что выведет код» с setTimeout и промисами — стандарт.
Промисы и async/await
Состояния промисов и цепочки, Promise.all/allSettled/race/any, обработка ошибок с await, параллельный запуск вместо последовательного.
Типы и приведение типов
Примитивы и объекты, == против ===, truthy/falsy, странности typeof, NaN, копирование по ссылке и по значению.
Модули и современный синтаксис
ES-модули против CommonJS, деструктуризация, spread, optional chaining, ??, а также новые API: structuredClone, toSorted, Object.groupBy, методы Set.
Память и сборщик мусора
GC на основе достижимости, типичные утечки в долгоживущих приложениях (обработчики, таймеры, отсоединённые DOM-узлы, кэши), когда помогают WeakMap и WeakRef.
DOM и браузерные API
Всплытие событий и делегирование, fetch с AbortController, варианты хранилищ и как не блокировать главный поток.
Вопросы и задачи по JavaScript на собеседовании по уровням
Junior
Чем отличаются var, let и const?
Что должно быть в сильном ответе:У var функциональная область видимости, и она поднимается со значением undefined, а let и const блочные и до строки объявления находятся в temporal dead zone, поэтому ранний доступ бросает ReferenceError. const запрещает переприсваивание переменной, а не изменение содержимого: объект или массив в const можно мутировать. Сильный ответ упоминает, что let чинит баг с замыканием в цикле, потому что каждая итерация получает новую переменную.
В чём разница между == и ===?
Что должно быть в сильном ответе:=== сравнивает без приведения типов, а == сначала применяет правила coercion, отсюда сюрпризы вроде 0 == "" → true. Стоит назвать полезное исключение: null == undefined даёт true, поэтому x == null проверяет оба значения, а NaN не равен ничему, даже себе (для проверки — Number.isNaN). По умолчанию — ===, а == null только осознанно.
Что такое замыкание? Приведите практический пример.
Что должно быть в сильном ответе:Замыкание — это функция вместе с лексическим окружением, в котором она создана, поэтому она читает и меняет внешние переменные даже после завершения внешней функции. На практике: приватное состояние (счётчик, кэш для memoize), фабрики функций, обработчики событий, которые помнят настройки. Хорошо сказать и о цене: замыкание удерживает захваченные объекты, что может привести к утечке памяти.
Что возвращает typeof null и как проверить, что значение — массив?
Что должно быть в сильном ответе:typeof null возвращает 'object' — исторический баг, оставленный ради совместимости, поэтому null проверяют явно через === null. Для массива typeof тоже даёт 'object', так что используют Array.isArray(value), который, в отличие от instanceof Array, работает и между iframe. Бонус: typeof функции — 'function', а typeof необъявленной переменной возвращает 'undefined' без ошибки.
Что такое делегирование событий и зачем оно нужно?
Что должно быть в сильном ответе:Большинство DOM-событий всплывают от целевого элемента вверх по предкам, поэтому можно повесить один обработчик на родителя и через event.target (часто с closest()) понять, по какому дочернему элементу кликнули. Это экономит память, работает для элементов, добавленных позже, и упрощает очистку. Стоит упомянуть, что focus не всплывает (есть focusin), и разницу между stopPropagation и preventDefault.
Middle
Что выведет код и почему: console.log(1); setTimeout(() => console.log(2), 0); Promise.resolve().then(() => console.log(3)); console.log(4);
Что должно быть в сильном ответе:Выведет 1, 4, 3, 2. Сначала выполняется синхронный код; колбэк .then попадает в очередь микрозадач, а setTimeout — в очередь задач (макрозадач). Когда текущий скрипт завершился, event loop полностью опустошает микрозадачи и только потом берёт следующую задачу, поэтому 3 выводится раньше 2 даже с задержкой 0 мс.
Как определяется значение this?
Что должно быть в сильном ответе:Для обычных функций this зависит от способа вызова: new привязывает новый объект, call/apply/bind задают его явно, obj.method() даёт obj, а простой вызов — undefined в strict mode (globalThis в нестрогом). У стрелочных функций нет своего this, они берут его из внешней области. Типичный баг — передать obj.method как колбэк и потерять this; лечится bind или обёрткой-стрелкой.
Сравните Promise.all, Promise.allSettled, Promise.race и Promise.any.
Что должно быть в сильном ответе:all возвращает все значения в порядке входных промисов и отклоняется на первой ошибке; allSettled ждёт всех и возвращает объекты со статусом, никогда не отклоняясь. race завершается вместе с первым завершившимся промисом, успехом или ошибкой; any берёт первый успешный и отклоняется с AggregateError, только если упали все. Ни один из них не отменяет оставшуюся работу — для этого нужен AbortController.
Как классы в JavaScript связаны с прототипами?
Что должно быть в сильном ответе:class в основном синтаксис над функциями-конструкторами: методы лежат в Class.prototype, а экземпляры находят их через цепочку [[Prototype]]; extends связывает и прототипы, и сами конструкторы. Отличия от обычных функций: тело класса всегда в strict mode, класс можно вызвать только через new, а объявление находится в TDZ до выполнения. Настоящие приватные поля (#field) и static-блоки — уже не просто сахар, а возможности только классов.
Debounce против throttle: в чём разница и как реализовать debounce?
Что должно быть в сильном ответе:Debounce вызывает функцию, только когда вызовы прекратились на N мс (поиск при вводе, конец resize); throttle — не чаще раза в N мс (scroll, mousemove). Debounce — это замыкание с id таймера: на каждый вызов сбрасываем предыдущий timeout и ставим новый, который вызовет исходную функцию с последними аргументами и this. Плюсом будет упомянуть опции leading/trailing и отмену отложенного вызова при размонтировании.
Senior
Как найти и исправить утечку памяти в SPA, которое работает часами?
Что должно быть в сильном ответе:GC освобождает только недостижимые объекты, поэтому утечка — это то, на что по ошибке осталась ссылка: неснятые обработчики и таймеры, замыкания с большими данными, отсоединённые DOM-узлы в переменных, неограниченные кэши или глобальные сторы. Диагностика: heap snapshots в DevTools до и после повторяемого действия, сравнение удерживаемых объектов и их retainer-цепочек. Исправления: снимать обработчики при размонтировании (сигнал AbortController снимает их пачкой), ограничивать кэши, для метаданных по ключу-объекту брать WeakMap.
Какие практические различия между ES-модулями и CommonJS?
Что должно быть в сильном ответе:Импорты ESM статические и резолвятся до выполнения, что даёт tree shaking, top-level await и живые привязки к экспортам; require в CommonJS — синхронный вызов во время выполнения, возвращающий объект module.exports. ESM всегда в strict mode и вместо __dirname использует import.meta. По совместимости: актуальные версии Node умеют require() синхронных ES-модулей, а модули с top-level await по-прежнему требуют import().
Тяжёлое вычисление подвешивает интерфейс. Как сохранить отзывчивость страницы?
Что должно быть в сильном ответе:JavaScript выполняется в главном потоке, а браузер рендерит и обрабатывает ввод только между задачами, поэтому длинные задачи портят отзывчивость и INP. Варианты: разбить работу на куски и отдавать управление event loop (scheduler.yield() там, где поддерживается, иначе setTimeout или MessageChannel), вынести тяжёлые вычисления в Web Worker, группировать чтение и запись DOM, чтобы избежать layout thrashing. Сначала измерить — во вкладке Performance или через Long Animation Frames API.
Какие типичные ловушки async/await в продакшн-коде?
Что должно быть в сильном ответе:Если await’ить независимые вызовы по очереди, они становятся последовательными — лучше запустить вместе и ждать через Promise.all. Пропущенный await или «потерянный» промис означает, что try/catch не увидит ошибку, и будет unhandled rejection. Array.forEach не ждёт async-колбэков: для последовательного выполнения — for...of, для параллельного — map плюс Promise.all; и помнить, что каждый await продолжается в микрозадаче, то есть позже, чем кажется по коду.
Как реализовать отмену и избежать race condition в асинхронных UI-сценариях?
Что должно быть в сильном ответе:Через AbortController: передавать signal в fetch и собственные async-функции и отменять предыдущий запрос, когда стартует новый (например, поиск при вводе), чтобы устаревший ответ не перезаписал свежий. AbortSignal.timeout() добавляет дедлайн, AbortSignal.any() объединяет сигналы. Стоит сказать, что отмена отклоняет промис с AbortError, который нужно обрабатывать отдельно от настоящих ошибок, и что тот же signal снимает обработчики событий.
Как проходит мок-собеседование по JavaScript
- Выберите JavaScript (и другие технологии из своего стека), свой уровень и язык собеседования: украинский, английский или русский. Или загрузите резюме и описание вакансии.
- Отвечайте на каждый вопрос голосом, как на настоящем собеседовании.
- Получите оценку каждого ответа по точности, полноте и понятности и отчёт со слабыми местами.
Как отвечать на вопросы по JavaScript вслух
- →Держитесь структуры: определение одним предложением, как это работает внутри, короткий пример. На «что такое замыкание» — секунд 30, а не три минуты.
- →В задачах «что выведет код» проговаривайте выполнение: сначала синхронный код, потом микрозадачи, потом следующая задача. Итоговый порядок назовите отдельно и чётко.
- →Код вслух читается плохо, поэтому описывайте его словами: «стрелочная функция, которая возвращает промис» лучше, чем диктовать скобки и точки с запятой.
- →Называйте крайние случаи до того, как спросят: NaN, null против undefined, strict mode, this в стрелочных функциях. Именно это отличает полный ответ от просто правильного.
- →Подкрепляйте теорию реальным случаем: потерянный this в колбэке, утечка из-за неснятого обработчика. Короткая история из опыта особенно важна на middle и senior.
Частые вопросы
Нужно ли писать код на этом собеседовании по JavaScript?
Нет. Редактора для live coding нет: вопросы устные и концептуальные, отвечаете голосом. Вас могут попросить предсказать результат фрагмента кода, объяснить, как бы вы реализовали debounce, или рассказать, как искали бы баг.
Можно потренировать TypeScript?
Да, TypeScript можно выбрать как отдельную технологию. Трек JavaScript сосредоточен на самом языке и среде выполнения: области видимости, this, event loop, асинхронный код, память и браузерные API.
Подойдёт ли это опытным разработчикам?
Выберите уровень senior или загрузите вакансию — вопросы смещаются к архитектуре асинхронного кода, производительности, утечкам памяти и модульным системам. Отчёт оценивает каждый ответ по технической точности, полноте и ясности, так что видно, где объяснения «проседают».
Сколько это стоит?
Первое собеседование (до 5 вопросов) бесплатно. Дальше — оплата за каждое собеседование, от $1.75, без подписки.