Как пройти собеседование по JavaScript: продвинутый уровень

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Как пройти собеседование по JavaScript: топ вопросов продвинутого уровня

Собеседование на позицию Senior JavaScript-разработчика или Node.js инженера — это не просто проверка синтаксиса. Интервьюеры хотят увидеть глубокое понимание языка: как работает Event Loop, что такое контекст this на самом деле, и как избежать "адской" вложенности при работе с асинхронностью. В этой статье мы разберём самые частые и коварные вопросы, которые задают на продвинутом уровне. Готовьтесь не просто читать ответы, а разбираться в механике.

1. Event Loop, стек вызовов и микротаски (Microtasks vs Macrotasks)

Это, пожалуй, самый популярный блок на собеседованиях по Node.js. Вопрос звучит так: "Что выведет этот код и в каком порядке?".

console.log('1');

setTimeout(() => console.log('2'), 0);

Promise.resolve().then(() => console.log('3'));

process.nextTick(() => console.log('4'));

console.log('5');

Правильный ответ: 1, 5, 4, 3, 2.

Разбор:

  • Синхронный код (1, 5) выполняется первым.
  • process.nextTick() — это микротаска самого высокого приоритета. Она выполняется сразу после текущей операции, но ДО любых других микротасок (Promise) и макротасок (setTimeout).
  • Promise.then() — тоже микротаска, но её приоритет ниже, чем у nextTick.
  • setTimeout(cb, 0) — макротаска. Она выполняется только после того, как стек вызовов и все микротаски (все nextTick и Promise) будут полностью очищены.

Как ответить правильно: Объясните, что Node.js имеет фазы Event Loop. Микротаски (process.nextTick и Promise) обрабатываются между фазами. Макротаски (setTimeout, setInterval, I/O) — внутри фаз. nextTick — это нестандартная, но очень важная очередь, которая может заблокировать Event Loop, если использовать её рекурсивно.

2. Контекст this и его "потеря"

Вопрос: "Как работает this в JavaScript? Приведите пример, где this теряется, и как это исправить".

Продвинутый уровень подразумевает, что вы знаете 4 правила:

  1. Новая привязка (new binding) — при вызове функции через new, this указывает на новый созданный объект.
  2. Явная привязка — call, apply, bind.
  3. Неявная привязка — когда функция вызывается как метод объекта (obj.method()).
  4. По умолчанию — глобальный объект (global в Node.js, window в браузере) или undefined в строгом режиме.

Пример потери контекста:

const user = {
  name: 'Alice',
  greet() {
    console.log(`Hello, ${this.name}`);
  }
};

const greetFn = user.greet;
greetFn(); // Hello, undefined

Почему? Мы присвоили метод переменной. Теперь greetFn() вызывается без контекста объекта (user). this привязывается к глобальному объекту (или undefined в strict mode).

Как исправить:

  • Использовать bind: const greetFn = user.greet.bind(user);
  • Использовать стрелочную функцию (она не имеет своего this, а берёт его из внешнего лексического окружения).
  • Вызывать через call/apply: greetFn.call(user).

Бонусный вопрос: "А что будет, если user.greet передать в setTimeout?" — Тот же эффект потери. Исправляется через bind или стрелочную функцию в колбэке.

3. Замыкания (Closures) и их практическое применение

Вопрос: "Объясните, что такое замыкание. Зачем оно нужно в реальной разработке на Node.js?"

Определение: Замыкание — это функция, которая "запоминает" свою лексическую область видимости (переменные внешней функции), даже когда эта внешняя функция уже завершила свою работу.

Классический пример:

function createCounter() {
  let count = 0;
  return function() {
    count++;
    return count;
  };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2

Переменная count не уничтожается после завершения createCounter, потому что внутренняя функция (замыкание) всё ещё держит на неё ссылку.

Продвинутое применение в Node.js:

  • Фабрики функций: Создание middleware для Express с заранее заданными параметрами (например, проверка прав доступа).
  • Мемоизация (кэширование результатов): Функция, которая запоминает результаты предыдущих вызовов для ускорения работы.
  • Приватные переменные: Создание "приватных" свойств в объектах до появления классов ES6 (и даже сейчас, для сокрытия внутреннего состояния).

Ловушка на собеседовании: Цикл с var и замыканиями.

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// Выведет: 3, 3, 3

Решение: заменить var на let (создаёт новую область видимости на каждой итерации) или использовать IIFE (Immediately Invoked Function Expression).

4. Async/Await и обработка ошибок: глубже, чем просто try/catch

Вопрос: "В чём разница между return await promise и return promise

Блоги

Рекомендаций по книгам