Фронтенд-собеседование почти никогда не проверяет память на синтаксис — его подсказывает редактор. Проверяют другое: понимаете ли вы, что происходит между кликом пользователя и отрисованным кадром, и умеете ли объяснить своё решение. Ниже — вопросы по темам с краткими ориентирами, что именно хочет услышать интервьюер. Формулировки не заучивайте: ценно, когда вы отвечаете своими словами и подкрепляете примером из практики.
Как устроена фронтенд-секция
Техническое интервью на middle и senior обычно складывается из четырёх частей. Теория по языку — модель выполнения, контекст, типы, прототипы. Лайвкодинг — небольшая задача на массивы, асинхронность или мини-компонент, где смотрят не столько на результат, сколько на ход мысли. Блок по фреймворку — чаще всего React: рендеры, эффекты, состояние. Вопросы по опыту — что вы чинили в проде, как ускоряли загрузку, почему выбрали такую архитектуру состояния.
Чем выше грейд, тем меньше вопросов «что такое» и больше «почему так» и «чем платите за это решение». Разберём блоки по порядку.
Event loop и асинхронность
- Как работает event loop? JavaScript выполняется в одном потоке: движок берёт задачу из очереди, выполняет её до конца, затем полностью опустошает очередь микрозадач и только потом переходит к следующей задаче. Пока выполняется задача, ничего другого не происходит — отсюда фризы интерфейса на тяжёлых вычислениях.
- Чем микрозадача отличается от макрозадачи? Микрозадачи — колбэки промисов,
queueMicrotask,MutationObserver; они выполняются раньше следующего таймера и раньше отрисовки. Макрозадачи —setTimeout, события, сетевые колбэки. Классическая проверка: порядок вывода в примере сsetTimeout,Promise.resolve().thenи синхроннымconsole.log. - Почему
setTimeout(fn, 0)не выполняется через ноль миллисекунд? Задержка задаёт минимальную границу, а не точное время: колбэк встанет в очередь и дождётся, пока освободится поток. Спецификация ещё и поднимает минимальную задержку у глубоко вложенных таймеров. Promise.all,allSettled,race,any— когда что?allпадает на первой ошибке и годится, когда нужны все результаты.allSettledдожидается всех и возвращает статусы — для параллельных независимых запросов.raceзавершается первым результатом любого типа,any— первым успешным.- Чем
awaitв цикле опасен? Он превращает параллельные запросы в последовательные. Ждут, что вы соберёте промисы и передадите их вPromise.all, а последовательность оставите там, где есть настоящая зависимость. - Как отменить запрос? Через
AbortControllerиsignal— вfetch, а также в собственных абстракциях над ним. На собеседовании это часто продолжение вопроса про гонки: что произойдёт, если пользователь быстро переключит фильтры и старый ответ придёт позже нового.
Замыкания, this и контекст
- Что такое замыкание? Функция вместе с областью видимости, в которой она создана: внешние переменные живут, пока жива функция. Ждут не определения, а примера — счётчик, приватное состояние, кэш, обработчик с захваченным значением.
- Как определяется
this? По способу вызова, а не по месту объявления: обычный вызов, вызов как метода, явная привязка черезcall,applyиbind, вызов сnew. Стрелочные функции своегоthisне имеют и берут его из внешней области — поэтому их удобно использовать в колбэках и нельзя как методы объекта, когда нужен сам объект. - Зачем нужен
bind, если есть стрелки?bindсоздаёт новую функцию с зафиксированным контекстом и умеет частично применять аргументы. Ещё он нужен там, где функцию нельзя переписать на стрелочную, — например при передаче существующего метода в обработчик. var,let,const— в чём разница?varимеет функциональную область видимости и всплывает со значениемundefined.letиconstживут в блоке и до объявления находятся в «временной мёртвой зоне» — обращение к ним бросает ошибку.constзапрещает переприсваивание, но не делает объект неизменяемым.- Классическая ловушка с циклом и таймером? Цикл с
varиsetTimeoutвыведет одно и то же значение, потому что переменная одна на всю функцию; сletна каждой итерации создаётся своя привязка. Вопрос проверяет понимание областей видимости, а не знание истории языка.
Типы, сравнение и приведение
- Чем
==отличается от===? Строгое сравнение не приводит типы, нестрогое — приводит по правилам спецификации. В рабочем коде почти всегда используют===; единственное частое исключение — проверкаvalue == null, которая покрывает иnull, иundefined. - Почему
NaN !== NaN? Так задано в стандарте IEEE 754 для «не числа». Проверять нужно черезNumber.isNaN, а для сравнения с учётомNaNи нулей разного знака — черезObject.is. - Почему
typeof nullвозвращает'object'? Историческая ошибка ранних реализаций, которую оставили ради обратной совместимости. Ждут, что вы знаете корректные проверки:value === null,Array.isArray,Object.prototype.toString.call. - Что такое примитивы и обёртки? Примитивы неизменяемы и не имеют методов; при обращении к методу движок временно создаёт объект-обёртку. Отсюда и вопрос про
new String('a') === 'a'— сравнение вернётfalse, потому что слева объект. - Чем
nullотличается отundefined?undefined— значение по умолчанию для необъявленного результата,null— явно записанная пустота. На практике важнее договорённость в команде и то, как оба варианта ведут себя с параметрами по умолчанию и оператором??. - Как скопировать объект? Спред и
Object.assignкопируют один уровень, вложенные объекты остаются общими. Для глубокой копии —structuredClone, с оговоркой, что функции и классы он не переносит.
Прототипы, классы и объекты
- Как работает прототипное наследование? У объекта есть ссылка на прототип; если свойство не найдено, поиск идёт вверх по цепочке до
null.prototype— свойство функции-конструктора, а не самого объекта; текущий прототип объекта читают черезObject.getPrototypeOf. class— это новая модель или синтаксис поверх прототипов? Синтаксис поверх прототипов, но с отличиями: тело класса всегда в строгом режиме, объявление не всплывает, вызов конструктора безnewбросает ошибку.- Зачем
Object.create(null)? Объект без прототипа — удобен как словарь: в нём не встретятся унаследованныеtoStringи другие свойства, а значит, нет коллизий с пользовательскими ключами. - Что дают геттеры, сеттеры и дескрипторы? Контроль над чтением и записью, а также флаги
writable,enumerable,configurable. Тема всплывает, когда речь заходит о реактивности: как библиотеки отслеживают изменения черезProxyи перехват операций. - Чем
Mapлучше объекта? Ключами могут быть любые значения, порядок вставки сохраняется, размер доступен напрямую, нет конфликтов с прототипом.WeakMapдополнительно не удерживает ключи от сборки мусора — типовой ответ на вопрос про кэш, привязанный к объектам.
Модули, сборка и рантайм
- ESM или CommonJS? ESM разбирается статически: импорты известны до выполнения, поэтому работают tree shaking и top-level await, а привязки остаются живыми. CommonJS вычисляет
requireв рантайме и возвращает копию значения на момент импорта. - Что такое tree shaking и почему он не сработал? Удаление неиспользуемого кода при сборке. Чаще всего мешают побочные эффекты модуля и неверный
sideEffectsвpackage.json— ждут, что вы объясните причину, а не назовёте термин. - Зачем code splitting и динамический импорт? Чтобы не грузить в первом бандле то, что нужно на других маршрутах. Обычная связка — разделение по роутам плюс ленивые тяжёлые компоненты вроде редакторов и графиков.
- Что происходит при
npm run build? Разбор графа модулей, транспиляция, минификация, разделение на чанки, хеши в именах файлов для кэширования. Хороший ответ упоминает исходные карты и то, зачем их отдают отдельно. - Чем отличаются SSR, SSG и CSR? Рендер на сервере на каждый запрос, генерация страниц при сборке и рендер в браузере. Ждут не определений, а выбора под задачу: что даёт SSR для поиска и первого экрана и чем он платит за это на сервере.
TypeScript
- Что такое структурная типизация? Совместимость определяется формой типа, а не именем: объект подходит, если у него есть нужные поля. Отсюда следует, что два независимо объявленных типа с одинаковыми полями взаимозаменяемы.
anyилиunknown?anyотключает проверки и распространяется по коду.unknownтоже принимает любое значение, но требует сузить тип перед использованием — безопасный вариант для внешних данных.- Проверяются ли типы в рантайме? Нет, они стираются при компиляции. Данные с сервера нужно валидировать отдельно — схемой или ручными проверками; ответ «типизировал ответ API, значит он такой» интервьюер считает ошибкой.
typeилиinterface? Интерфейсы расширяются и объединяются при повторном объявлении, псевдонимы типов умеют объединения, пересечения и вычисляемые типы. В командах обычно принимают соглашение и не спорят на каждом ревью.- Что такое размеченные объединения? Объединение объектов с общим полем-дискриминатором, по которому компилятор сужает тип в ветвлении. Основной инструмент для состояний загрузки, результатов и ошибок вместо набора необязательных полей.
- Зачем дженерики и
satisfies? Дженерики сохраняют связь между входом и выходом вместо возвратаany. Операторsatisfiesпроверяет значение на соответствие типу, не расширяя его, — удобно для конфигов, где нужны и проверка, и точные литеральные типы.
React
- Как работает реконсиляция и зачем
key? React сравнивает новое дерево элементов с предыдущим и переиспользует узлы.keyговорит, какой элемент какому соответствует; индекс массива в роли ключа ломает состояние строк при вставках и сортировке. - Когда эффект не нужен? Для вычисляемых значений, синхронизации состояния между собой и обработки событий пользователя. Официальная документация посвящает этому отдельную главу: эффект нужен для синхронизации с внешним миром, а не для деривации данных.
- Почему компонент лишний раз перерисовывается? Новая ссылка на объект или функцию в пропсах, обновление контекста, состояние подняли выше нужного. Ждут, что вы сначала измерите профайлером, а потом решите — перенести состояние ниже, разделить контекст или мемоизировать.
- Зачем
useMemoиuseCallbackи когда они лишние? Чтобы сохранить ссылку или не пересчитывать дорогое значение. На дешёвых вычислениях они добавляют шум и собственную стоимость; в проектах с подключённым компилятором React часть такой мемоизации расставляется автоматически. - Чем
useStateотличается отuseReducer? Первый хорош для независимых значений, второй — когда переходы состояния связаны правилами и их удобнее описать одним местом. Senior обычно продолжает про то, где хранить серверные данные и почему для них берут отдельный слой кэширования. - Что дают Server Components и Suspense? Server Components выполняются на сервере и не попадают в клиентский бандл, Suspense описывает состояние ожидания декларативно. Ждут понимания границы «сервер — клиент»: где можно обратиться к базе, а где нужен
use client.
Браузер, сеть и производительность
- Как работает CORS? Ограничение накладывает браузер, а не сервер: запрос уходит, но прочитать ответ скрипту разрешат только при нужных заголовках. Для небезопасных методов и заголовков сначала отправляется предварительный запрос
OPTIONS. - Где хранить токен? В
localStorageон доступен любому скрипту на странице, поэтому XSS сразу превращается в кражу сессии. Cookie с флагамиHttpOnly,SecureиSameSiteзащищённее, но требует защиты от CSRF. Правильный ответ — про компромисс, а не про единственный верный вариант. - Как защититься от XSS? Не вставлять недоверенный HTML, экранировать вывод, включить Content Security Policy. В React отдельный маркер — осознанное отношение к
dangerouslySetInnerHTML. - Что такое Core Web Vitals? LCP — время отрисовки основного контента, CLS — визуальная стабильность, INP — отзывчивость на взаимодействия, метрика, заменившая FID. Ждут, что вы смотрели их не только в синтетическом тесте, но и на реальных пользователях.
- Как ускорить загрузку страницы? Сократить и разделить бандл, отложить некритичные скрипты, оптимизировать изображения и шрифты, добавить кэширование и предзагрузку ключевых ресурсов. Хороший ответ начинается с измерения и указывает, что именно тормозило.
- Зачем делегирование событий? Один обработчик на контейнере вместо сотен на элементах: меньше памяти и работает для динамически добавленных узлов. Здесь же обычно спрашивают про всплытие, перехват и
passive-слушатели у прокрутки.
Лайвкодинг
Задачи редко бывают олимпиадными. Чаще просят сгруппировать и агрегировать массив данных, реализовать debounce или throttle, написать простой кэш или повтор запроса с задержкой, собрать небольшой компонент с загрузкой и состоянием ошибки. Смотрят на три вещи: уточняете ли вы условие до начала, проговариваете ли ход мысли, вспоминаете ли про краевые случаи — пустой ввод, гонку ответов, отмену.
Работающее решение с озвученным планом улучшения ценится выше молчаливого поиска идеального варианта. Если застряли, скажите об этом вслух и предложите более простой путь: интервьюер оценивает мышление, а не скорость набора.
Чего ждут от middle, а чего от senior
Middle уверенно применяет язык и фреймворк: объясняет порядок выполнения микрозадач, не путается в this, знает, зачем key, умеет найти лишний ререндер профайлером и сузить тип вместо any. Достаточно понимать механизмы и делать разумный выбор инструмента.
Senior отвечает на те же вопросы через компромиссы и последствия. Не «что такое code splitting», а «почему разбили именно так и что это дало метрикам». Ждут историй: как искали утечку слушателей, почему отказались от глобального стора, как переписали загрузку данных и что показал мониторинг. Ещё ждут архитектурного разговора — соседний уровень подготовки разобран в гайде про System Design на собеседовании.
Как готовиться
Пройдите темы списком: по каждому блоку выше проговорите ответ вслух, будто перед вами интервьюер. Места, где мысль звучит связно только в голове, — ваши слабые точки. Параллельно тренируйте лайвкодинг с комментированием хода мысли и подготовьте две-три истории из своей практики: что чинили, какие варианты рассматривали, чем всё закончилось.
Дальше стоит посмотреть на смежные секции. Если в вакансии есть бэкенд, помогут разборы вопросов по Python и Go — устроены так же, по темам с ориентирами. А до технической части почти всегда есть скрининг по резюме: как читает его нанимающий менеджер, разобрано в статье про разбор резюме разработчика. Хотите увидеть процесс целиком — начните с обзора «Как проходит техническое собеседование».
Ускоряет подготовку тренировка с языковой моделью: попросите её сыграть интервьюера и гонять вас по темам — рабочие промпты и ограничения подхода собраны в гайде про подготовку к собеседованию с LLM. Но модель хвалит охотнее, чем стоит, и не воспроизводит давление живого диалога. Поэтому за несколько дней до реального интервью возьмите мок-собеседование с практикующим инженером: он копает в ответ до конца и подсвечивает пробелы, которые сам за собой не замечаешь.
В сообществе IT-ХОЗЯЕВА мок-собеседования и доступ к базе менторов входят в тариф ХОЗЯИН за 2000 ₽/мес — там же разбирают резюме и готовят под конкретную вакансию. Это удобно, когда нужна не разовая сессия, а регулярная практика с обратной связью от людей, которые сами проводят интервью.
Фронтенд-секция вознаграждает понимание, а не заученные определения. Разберитесь, как код доходит до кадра на экране, потренируйтесь объяснять это вслух — и неожиданный вопрос превратится в разговор, а не в провал.