Вопросы на собеседовании: Фронтенд-разработчик
100 реальных вопросов с образцовыми ответами и пояснениями для уровня Middle.
Смотреть пример резюме: Фронтенд-разработчик →Вопросы
Цикл событий опустошает всю очередь микрозадач после каждой макрозадачи, до следующего кадра отрисовки.
- Микрозадачи: Promise, queueMicrotask, MutationObserver; все они выполняются до отрисовки.
- Макрозадачи: setTimeout, setInterval, I/O; setTimeout(..., 0) уступает очереди отрисовки.
- Длинные цепочки .then() могут задушить рендеринг и заморозить интерфейс.
- Знание порядка позволяет предсказывать выполнение и избегать рывков.
Зачем это спрашивают: Проверяет, понимает ли кандидат параллелизм JavaScript глубже, чем просто знание того, что язык однопоточный.
Замыкание - это функция, сохраняющая доступ к переменным объемлющей области даже после завершения её выполнения.
- Оно захватывает внешние переменные по ссылке, а не копией.
- Классический пример: фабрика makeCounter, возвращающая increment и decrement с одной приватной count.
- Оно лежит в основе паттерна модуля и хелперов мемоизации.
Зачем это спрашивают: Оценивает, способен ли кандидат применить концепцию на практике, а не просто воспроизвести учебное определение.
Все три явно задают this функции, но различаются тем, как и когда вызывают её.
- call: вызывает немедленно, аргументы перечисляются по отдельности.
- apply: вызывает немедленно, аргументы передаются массивом.
- bind: возвращает новую функцию с постоянно привязанным this, не вызывая её.
- bind удобен для передачи методов класса в коллбэки без потери контекста.
Зачем это спрашивают: Проверяет практическое понимание привязки this, а не просто знание синтаксиса.
Каждый объект ссылается на другой через [[Prototype]], образуя цепочку, заканчивающуюся на Object.prototype.
- Поиск свойства обходит цепочку, пока не найдёт его или не дойдёт до конца.
- Синтаксис класса ES6 - сахар над той же прототипной моделью наследования.
- Методы класса попадают в prototype конструктора, а не в каждый экземпляр.
Зачем это спрашивают: Выявляет, понимает ли кандидат, что классы не вводят новую модель наследования, - это важно при отладке неожиданного поведения прототипов.
Они различаются тем, когда завершаются и как обрабатывают отказы.
- Promise.all: выполняется, когда выполнены все, отклоняется при первом отказе.
- Promise.allSettled: ждёт завершения всех, возвращает объекты-статусы независимо от результата.
- Promise.race: завершается, как только завершится первый промис, выполнением или отказом.
- Promise.any: выполняется при первом успехе, отклоняется только если отклонились все.
Зачем это спрашивают: Проверяет, умеет ли кандидат выбрать нужный комбинатор для конкретного сценария асинхронной координации.
WeakMap и WeakSet нужны, когда ключи должен иметь право удалить сборщик мусора.
- Они хранят слабые ссылки, поэтому объект удаляется, как только нет сильных ссылок.
- Идеальны для метаданных о DOM-узлах или объектах без блокировки сборки мусора.
- У них нет методов итерации, поэтому не подходят, когда нужно перебирать записи.
Зачем это спрашивают: Оценивает понимание управления памятью в долгоживущих одностраничных приложениях.
Debounce ждёт паузу, throttle ограничивает частоту; выбор зависит от события.
- Debounce: срабатывает только после остановки вызовов на заданное время, идеален для поиска по мере ввода.
- Throttle: срабатывает не чаще раза за интервал, идеален для обработчиков scroll или resize.
- Debounce на scroll ощущается хуже throttle, потому что ничего не срабатывает во время прокрутки.
Зачем это спрашивают: Оценивает практическое знание паттернов производительности для высокочастотных браузерных событий.
Мемоизация кэширует результат чистой функции по её аргументам, чтобы повторные вызовы возвращались мгновенно.
- Реализуется через Map или объект-кэш в замыкании.
- Главный компромисс: память растёт с числом уникальных комбинаций аргументов и может течь при объектных ключах.
- В React useMemo и useCallback встроены, но добавляют накладные расходы при частой смене зависимостей.
Зачем это спрашивают: Проверяет умение соотносить пользу оптимизации производительности с затратами памяти.
Обычные функции получают свой this из места вызова, стрелочные наследуют его лексически.
- Обычные: this зависит от способа вызова (метод, конструктор, call/apply или по умолчанию).
- Стрелочные: захватывают this из объемлющей области в момент определения.
- Поэтому стрелочные ненадёжны как методы объектов, но идеальны как коллбэки внутри методов класса.
Зачем это спрашивают: Частый источник багов в реальных кодовых базах; проверяет, может ли кандидат быстро диагностировать ошибки, связанные с this.
Они различаются глубиной копирования и тем, что умеют сохранять.
- Spread: поверхностная копия, дублируются только ссылки верхнего уровня.
- JSON.parse(JSON.stringify()): глубокая копия, но теряет функции, превращает Date в строки, удаляет undefined, падает на циклах.
- structuredClone: настоящая глубокая копия с Date, типизированными массивами и циклами, но без функций и DOM-узлов.
Зачем это спрашивают: Проверяет знание ловушек, вызывающих тонкие баги при работе с вложенным состоянием или сложными структурами данных.
async/await - синтаксический сахар над Promise.
- Async-функция всегда возвращает Promise.
- await приостанавливает функцию и уступает циклу событий до выполнения ожидаемого Promise, затем возобновляет работу.
- Babel компилирует его в машину состояний с генераторной семантикой и цепочками .then().
- await над не-Promise работает, потому что значение оборачивается в Promise.resolve().
Зачем это спрашивают: Убеждается, что кандидат понимает async/await как надстройку над Promise, предотвращая заблуждения о блокирующем поведении.
Они различаются временем загрузки, структурой и семантикой связывания.
- CommonJS: require/module.exports, синхронная загрузка, вычисление в рантайме, изменяемые экспорты.
- ES-модули: import/export, статическая структура, анализируемая при парсинге, что позволяет tree shaking.
- В браузере ES-модули всегда асинхронны; Node поддерживает оба.
- Живые байндинги ES-модулей означают, что импорт отражает актуальное значение экспорта.
Зачем это спрашивают: Важно для понимания того, почему некоторые библиотеки не поддерживают tree shaking и почему смешивание модульных систем вызывает проблемы в бандлерах.
Генератор использует function* и может приостанавливаться через yield, возвращая итератор.
- Он выдаёт значения по требованию, не вычисляя всё наперёд.
- Полезен для пагинации при бесконечном скролле.
- Полезен для сложного асинхронного управления потоком в библиотеках вроде redux-saga.
- Полезен для ленивых последовательностей.
Зачем это спрашивают: Проверяет глубину знания JavaScript; генераторы - нишевый, но мощный инструмент, отличающий кандидатов с широкими знаниями от тех, кто использует только распространённые паттерны.
Находите утечку снимками кучи, затем чините источник, удерживающий ссылки живыми.
- Частые причины: глобальные слушатели, которые не удаляются; замыкания с большими данными в области модуля; неотменённые подписки при размонтировании.
- Обнаружение: делайте снимки кучи во вкладке Memory Chrome DevTools до и после навигации, сравнивайте удержанные размеры.
- Исправление: удаляйте слушателей в cleanup-функциях, используйте WeakMap для метаданных, отписывайтесь от WebSocket и Observable при размонтировании.
Зачем это спрашивают: Оценивает навык продакшн-отладки и знание специфических для долгоживущих SPA проблем с памятью.
Временная мёртвая зона - это промежуток между входом в область видимости и достижением объявления let или const.
- Обращение к переменной в её TDZ бросает ReferenceError, хотя она уже существует в области.
- Это отличается от var, которая поднимается и инициализируется значением undefined.
- Ошибки TDZ возникают в инициализаторах полей класса и при чтении переменной до её объявления.
Зачем это спрашивают: Отличает кандидатов с глубоким пониманием механики hoisting от тех, кто просто знает, что let и const ведут себя иначе, чем var.
React сравнивает старое и новое виртуальные DOM-деревья (diffing), чтобы решить, что перерендерить.
- Предполагается, что элементы разных типов дают разные деревья, а совпадающие поддеревья пропускаются.
- Проп key сигнализирует об идентичности элемента между рендерами, поэтому DOM-узел переиспользуется при смене позиции в списке.
- Без правильных ключей React может уничтожать и пересоздавать дорогие компоненты, теряя производительность и локальное состояние.
Зачем это спрашивают: Фундаментально для понимания производительности React; кандидат с этим знанием может диагностировать большинство багов с лишними ре-рендерами.
useCallback мемоизирует ссылку на функцию, useMemo - вычисленное значение.
- useCallback: сохраняет стабильную ссылку на функцию, чтобы мемоизированные дети не ре-рендерились зря.
- useMemo: избегает дорогих пересчётов при каждом рендере.
- useCallback - для стабильных пропсов мемоизированным детям; useMemo - для дорогих вычислений.
- Оба часто используют избыточно, добавляя накладные расходы при частой смене зависимостей.
Зачем это спрашивают: Проверяет суждение о том, когда мемоизация реально помогает, а не просто знание существования этих хуков.
Управляемый компонент ведётся состоянием React, неуправляемый хранит своё значение через ref.
- Управляемый: значение из состояния плюс обработчик onChange, поэтому React - источник истины.
- Неуправляемый: состояние живёт в DOM, читается через inputRef.current.value.
- Управляемый подходит для валидации, условного рендеринга и синхронизации нескольких полей.
- Неуправляемый проще для file-инпутов или сторонних DOM-библиотек.
Зачем это спрашивают: Выявляет понимание потока данных и обработки форм - одного из наиболее частых источников багов в React-приложениях.
Хуки должны вызываться на верхнем уровне React-функции, в одинаковом порядке при каждом рендере.
- Не вызывайте их внутри циклов, условий или вложенных функций.
- Вызывайте их только из React-компонентов или кастомных хуков, не из обычного JavaScript.
- React сопоставляет каждый хук с его состоянием по порядку вызова; нарушение порядка путает состояние.
Зачем это спрашивают: Понимание причин правил предотвращает трудно диагностируемые баги, вызванные условными вызовами хуков.
React.memo пропускает ре-рендер компонента, когда его пропсы поверхностно равны.
- Он не помогает, когда пропсы - объекты или функции, пересоздаваемые при каждом рендере родителя.
- Поверхностное сравнение видит эти новые ссылки как изменённые.
- Сочетайте его с useCallback и useMemo в родителе, чтобы оптимизация работала.
- Накладные расходы сравнения напрасны, если компонент и так дёшев в рендеринге.
Зачем это спрашивают: Проверяет, умеет ли кандидат определять, когда мемоизация контрпродуктивна, а не применяет её вслепую.
Закрытые вопросы
- 21
Как работают error boundaries в React и что они не перехватывают?
react - 22
Какие проблемы производительности есть у React Context?
reactperformance - 23
Когда предпочесть useReducer вместо useState?
hooks - 24
Что такое React.lazy и как Suspense поддерживает разделение кода?
react - 25
Что изменилось в батчинге состояния в React 18?
reactbatch - 26
Почему использование индекса массива в качестве пропа key в списке проблематично?
indexes - 27
Как реализовать виртуализацию для длинного списка и когда она необходима?
virtualization - 28
Для чего нужна функция очистки useEffect и чем она отличается от componentWillUnmount?
componentshooks - 29
Что такое React.forwardRef и когда он нужен?
react - 30
Как диагностировать медленный рендер с помощью React DevTools Profiler?
react - 31
Что такое паттерн составного компонента (Compound Component) и какую проблему он решает?
components - 32
Как расшарить состояние между двумя компонентами-сиблингами, не имеющими близкого общего родителя?
components - 33
Как возникают устаревшие замыкания в useEffect и как их исправить?
hooksclosures - 34
Что такое конкурентный рендеринг в React 18 и как он меняет поведение UI?
reactconcurrency - 35
Что такое паттерн render props и как он соотносится с кастомными хуками?
hooks - 36
В чём разница между interface и type alias в TypeScript и когда что выбирать?
typescripttypes - 37
Как использовать дженерики в React-компоненте, чтобы он работал с разными типами данных?
reactcomponentsgenerics - 38
Что такое условные типы в TypeScript и каков практический пример?
typescript - 39
Что такое ключевое слово infer в TypeScript и как его использовать?
typescript - 40
Что такое сужение типов (type narrowing) в TypeScript и какие техники можно применять?
typescript - 41
Что такое дискриминированные объединения в TypeScript и почему они полезны?
typescript - 42
Как добавить типы для сторонней JavaScript-библиотеки без TypeScript-определений?
typescriptjavascript - 43
Как написать перегрузку функции в TypeScript для разных комбинаций аргументов?
typescript - 44
Когда использовать CSS Grid, а когда Flexbox?
cssflexboxgrid - 45
Как работает специфичность CSS и как разрешать конфликты, не прибегая к !important?
css - 46
Что такое CSS-кастомные свойства и чем они отличаются от переменных Sass или Less?
css - 47
Что такое контекст наложения CSS (stacking context) и почему он влияет на z-index?
cssindexes - 48
В чём разница между единицами em, rem, vh, vw и ch и когда применять каждую?
css - 49
Как построить адаптивный макет без медиазапросов, используя современный CSS?
cssresponsivequeries - 50
Как на практике предотвращать смещение макета (Cumulative Layout Shift)?
web-vitals - 51
Что такое CSS containment и когда его стоит применять?
css - 52
Что такое Core Web Vitals и как их измерять в продакшне?
web-vitals - 53
Какие стратегии вы используете для уменьшения размера JavaScript-бандла?
bundle-sizejavascript - 54
Как работает tree shaking и каковы типичные причины его неэффективности?
tree-shaking - 55
В чём разница между Webpack и Vite при разработке и почему Vite быстрее?
webpackvite - 56
Что такое Hot Module Replacement и как он сохраняет состояние компонента?
componentshmr - 57
Как работают source maps и когда их следует включать в продакшне?
source-maps - 58
Как анализировать frontend-бандл для поиска узких мест производительности?
performance - 59
В чём разница между линтером и форматером и как они дополняют друг друга?
linting - 60
Как безопасно настроить переменные окружения в frontend-сборке?
soft-skillsconfig - 61
Что такое разделение кода (code splitting) и как его реализовать в React-приложении?
react - 62
Как работает кэширование браузера и как гарантировать, что пользователи получат последний код после деплоя?
cachingdeployment - 63
Какие форматы изображений использовать в разных случаях и как выбирать между ними?
images - 64
Как оптимизировать веб-шрифты для снижения смещения макета и блокировки рендеринга?
optimizationweb-vitals - 65
В чём разница между SSR, SSG, CSR и ISR и когда выбирать каждый подход?
ssr - 66
В чём разница между unit-, integration- и end-to-end-тестами во фронтенд-разработке?
e2e - 67
Как тестировать React-компонент, выполняющий API-запросы, без обращения к реальному серверу?
reactcomponentsapi - 68
Как тестировать кастомные React-хуки?
reacthooks - 69
Что означает «тестировать поведение, а не реализацию» и как это применять на практике?
- 70
Когда snapshot-тестирование полезно, а когда становится проблемой?
snapshot - 71
Что такое визуальное регрессионное тестирование и какие инструменты его поддерживают?
visual - 72
Как обрабатывать асинхронные обновления состояния в тестах, чтобы избежать нестабильных утверждений?
soft-skillsasynctesting - 73
Что такое архитектура Flux и как Redux её реализует?
statearchitecture - 74
Каковы компромиссы между Redux Toolkit, Zustand и React Query для управления состоянием?
reactstatequeries - 75
В чём разница между серверным состоянием и UI-состоянием и почему их нужно управлять отдельно?
state - 76
Как реализовать оптимистичные обновления UI?
locking - 77
Что такое prop drilling и какие существуют решения?
state - 78
Как сохранять состояние между перезагрузками страницы?
state - 79
Как проектировать масштабируемую структуру папок для большого React-приложения?
designstructurereact - 80
Что такое дизайн-система и что она включает помимо библиотеки компонентов?
componentsdesign-systemsystem-design - 81
Что такое паттерн BFF (Backend for Frontend) и когда он полезен?
- 82
Когда стоит рассматривать микрофронтенды и каковы основные компромиссы?
micro-frontends - 83
Как управлять ломающими изменениями API на фронтенде при эволюции бэкенда?
api - 84
Как диагностировать компонент, который ре-рендерится чаще, чем ожидается?
components - 85
Как отладить утечку памяти в React-приложении?
memoryreact - 86
Регрессия производительности появилась в продакшне, но не воспроизводится локально. Как вы будете расследовать?
performance - 87
API-вызов нестабильно падает в продакшне, но стабильно работает в разработке. Как вы расследуете?
api - 88
CSS работает в Chrome, но ломается в Safari. Как вы подходите к отладке?
css - 89
Как отладить ошибку несовпадения гидратации в серверном React-приложении?
reacthydration - 90
Какие инструменты вы используете для диагностики медленного выполнения JavaScript?
javascript - 91
В чём разница между ARIA-ролями и нативными HTML-семантическими элементами для доступности?
htmla11y - 92
Как обеспечить корректную навигацию с клавиатуры внутри модального диалога?
a11y - 93
Что такое коэффициент контрастности цвета и как проверить соответствие стандартам WCAG?
a11y - 94
Как сделать динамически обновляемую область контента доступной для скринридеров?
a11y - 95
Как реализовать полностью доступный кастомный компонент выпадающего списка?
components - 96
Расскажите, как вы выявили и устранили значительную проблему производительности в продакшн-приложении.
performance - 97
Как вы подходите к задаче, где требования неясны или противоречат друг другу?
soft-skills - 98
Как вы реагируете на ревью кода, где категорически не согласны с отзывом ревьюера?
soft-skillsconflictfeedback - 99
Как вы подходите к рефакторингу крупного легаси-компонента без нарушения существующего поведения?
refactoringcomponents - 100
Как вы следите за быстро меняющейся экосистемой фронтенда, не тратя на это всё своё время?
learning