Frontend: JavaScript, TypeScript, React: вопросы на собеседовании

Event loop, замыкания, прототипы, рендеринг в браузере, React-хуки, TypeScript, безопасность на клиенте.

#1JavaScript: основыjunior

В чём разница между var, let и const?

Область видимости. var — функциональная, let и const — блочная: переменная живёт внутри {}.

Всплытие (hoisting). Все три объявления поднимаются наверх области видимости, но var сразу инициализируется значением undefined, а let/const попадают во временную мёртвую зону (TDZ) — обращение до строки объявления бросает ReferenceError.

Переприсваивание. const запрещает переприсваивать саму привязку, но не запрещает менять содержимое объекта: const a = []; a.push(1) — валидно.

Повторное объявление. var x дважды в одной области — можно, let x — SyntaxError.

В современном коде правило простое: по умолчанию const, let — когда значение меняется, var — не используем.

  • Что выведет цикл с var и setTimeout внутри и почему?
  • Что такое TDZ?
#2JavaScript: основыjunior

Что такое замыкание? Приведите практический пример.

Замыкание — функция вместе с сохранённой ссылкой на лексическое окружение, в котором она была создана. Функция «помнит» переменные внешней области даже после того, как внешняя функция завершилась.

function createCounter() {
  let count = 0;            // живёт в замыкании
  return () => ++count;
}
const next = createCounter();
next(); // 1
next(); // 2

Зачем на практике:

  • приватное состояние без классов (модульный паттерн);
  • debounce/throttle хранят таймер в замыкании;
  • каррирование и фабрики функций;
  • хуки React (useState) работают на замыканиях.

Классическая ловушка: цикл с var и setTimeout напечатает три раза 3, потому что все колбэки замкнуты на одну переменную. С let каждая итерация создаёт новую привязку — будет 0, 1, 2.

  • Чем замыкание может навредить памяти?
  • Как реализовать функцию once()?
#3JavaScript: event loopmiddle

Как работает Event Loop? Что выведет код с setTimeout и Promise?

JavaScript однопоточный: есть стек вызовов, очередь макрозадач (таймеры, события, сетевые колбэки) и очередь микрозадач (Promise.then, queueMicrotask, MutationObserver).

Цикл событий: выполнить текущую задачу до конца → полностью опустошить очередь микрозадач → при необходимости отрисовать кадр → взять следующую макрозадачу.

console.log(1);
setTimeout(() => console.log(2), 0);
Promise.resolve().then(() => console.log(3));
console.log(4);
// 1, 4, 3, 2

1 и 4 — синхронный код. 3 — микрозадача, она выполняется сразу после синхронного кода. 2 — макрозадача, она последняя.

Следствие: бесконечная генерация микрозадач заблокирует отрисовку, а тяжёлый синхронный цикл заморозит вкладку — потому что он не отпускает стек.

  • Чем отличается requestAnimationFrame от setTimeout(0)?
  • Что такое starvation микрозадачами?
#4JavaScript: основыjunior

Как определяется значение this в JavaScript?

Значение this определяется способом вызова, а не местом объявления. Четыре правила по убыванию приоритета:

  1. `new` — this это новый создаваемый объект.
  2. Явная привязка — call, apply, bind задают this вручную.
  3. Вызов как метода — obj.method(): this это obj.
  4. Свободный вызов — this это undefined в strict mode (в модулях всегда strict) и globalThis вне него.

Стрелочные функции не имеют собственного this — они берут его лексически из места объявления, и bind на них не действует. Поэтому колбэк в классе удобно писать стрелкой:

class Timer {
  seconds = 0;
  tick = () => { this.seconds++; };  // this всегда экземпляр
}

Типичная ошибка: const fn = obj.method; fn(); — потеря контекста, this становится undefined.

  • Чем call отличается от apply и bind?
  • Почему bind на стрелочной функции не работает?
#5JavaScript: прототипыmiddle

Что такое прототипное наследование? Чем prototype отличается от __proto__?

У каждого объекта есть скрытая ссылка на другой объект — прототип. Если свойство не найдено в самом объекте, движок идёт по цепочке прототипов вверх до null.

  • __proto__ (правильнее — Object.getPrototypeOf(obj)) — ссылка самого объекта на его прототип.
  • prototype — свойство функции-конструктора: объект, который станет прототипом для всех экземпляров, созданных через new.
function User(name) { this.name = name; }
User.prototype.hello = function () { return `Привет, ${this.name}`; };

const u = new User('Аня');
Object.getPrototypeOf(u) === User.prototype; // true
u.hello(); // метод найден в прототипе

class в JS — синтаксический сахар над этим механизмом: методы класса кладутся в prototype, extends выстраивает цепочку. Поэтому методы не дублируются в каждом экземпляре — экономия памяти.

  • Что делает Object.create(null) и зачем?
  • Как работает instanceof?

Ещё 15 вопросов в этом треке

  1. Чем == отличается от ===? Приведите неочевидные примеры приведения типов.
  2. Как всплывают события в DOM и что такое делегирование?
  3. Чем различаются Promise.all, allSettled, race и any?
  4. Как async/await соотносится с промисами? Как правильно обрабатывать ошибки?
  5. Чем debounce отличается от throttle? Где применяется каждый?
  6. Что такое Virtual DOM и зачем нужен атрибут key в списках?
  7. Как работает useEffect? Что такое массив зависимостей и функция очистки?
  8. Когда действительно нужны useMemo и useCallback?
  9. Чем управляемый компонент отличается от неуправляемого?
  10. Чем type отличается от interface? Что выбрать?
  11. В чём разница между any, unknown и never?
  12. Что такое reflow и repaint? Как уменьшить их количество?
  13. Как рассчитывается специфичность CSS-селекторов?
  14. Чем localStorage отличается от sessionStorage и cookies?
  15. Что такое CORS и почему возникает ошибка «blocked by CORS policy»?