Как пройти собеседование по JavaScript: топ вопросов
Собеседование на позицию JavaScript-разработчика (особенно с фокусом на Node.js) — это не просто проверка синтаксиса. Работодатели хотят видеть глубокое понимание асинхронности, областей видимости и работы Event Loop. В этой статье мы собрали топ вопросов, которые действительно задают на собеседованиях, с примерами кода и пояснениями.
1. Замыкания (Closures) — основа функционального JS
Вопрос: "Что такое замыкание и как оно работает в Node.js?"
Замыкание — это функция, которая "запоминает" своё лексическое окружение даже после того, как внешняя функция завершила выполнение. Это ключевой механизм для инкапсуляции данных в JavaScript.
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
Почему это спрашивают: Замыкания лежат в основе модулей, фабрик функций и обработчиков событий в Node.js. Без понимания замыканий невозможно работать с асинхронными колбэками.
2. Event Loop и асинхронность (Async/Await, Promises)
Вопрос: "Объясните, как работает Event Loop. Что выведет этот код?"
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
Ответ: 1, 4, 3, 2. Сначала синхронный код, затем микрозадачи (Promise.then), затем макрозадачи (setTimeout).
Event Loop — это механизм, который позволяет Node.js выполнять неблокирующие операции, несмотря на однопоточность. Он состоит из нескольких фаз:
- Таймеры — выполняют колбэки setTimeout/setInterval
- I/O колбэки — обработка завершённых операций ввода-вывода
- Poll (опрос) — получение новых I/O событий
- Check — выполнение setImmediate()
- Close callbacks — закрытие соединений
Совет: На собеседовании покажите понимание разницы между process.nextTick() и setImmediate(). nextTick выполняется перед следующей фазой Event Loop, а setImmediate — в фазе Check.
3. Контекст вызова (this) и стрелочные функции
Вопрос: "Чему равен this в разных ситуациях? Как изменить контекст?"
Значение this определяется тем, как вызвана функция, а не где она объявлена. В Node.js (строгий режим) в обычной функции this будет undefined, а в браузере — window.
const obj = {
name: 'Node.js',
regularFunc: function() {
console.log(this.name);
},
arrowFunc: () => {
console.log(this.name);
}
};
obj.regularFunc(); // 'Node.js'
obj.arrowFunc(); // undefined (this берётся из внешнего контекста)
Как управлять контекстом:
call(thisArg, arg1, arg2)— вызывает функцию с указанным this и аргументамиapply(thisArg, [args])— аналогично, но аргументы передаются массивомbind(thisArg)— создаёт новую функцию с привязанным this- Стрелочные функции — не имеют собственного this, используют лексический контекст
4. Прототипы и наследование (Prototype Chain)
Вопрос: "Как работает прототипное наследование в JavaScript? В чём отличие от классов?"
JavaScript использует прототипное наследование: каждый объект имеет внутреннюю ссылку [[Prototype]] (доступна через __proto__ или Object.getPrototypeOf()).
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(`${this.name} издаёт звук`);
};
function Dog(name, breed) {
Animal.call(this, name);
this.breed = breed;
}
// Устанавливаем прототип
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
console.log('Гав!');
};
const rex = new Dog('Рекс', 'Овчарка');
rex.speak(); // "Рекс издаёт звук"
rex.bark(); // "Гав!"
Современный синтаксис (ES6 классы):
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} издаёт звук`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
bark() {
console.log('Гав!');
}
}
Важно: Классы — это "синтаксический сахар" над прототипами. Они не вводят новую модель наследования, а лишь упрощают запись.
5. Обработка ошибок в Node.js
Вопрос: "Как правильно обрабатывать ошибки в асинхронном коде?"
В Node.js существует несколько паттернов:
- Callback style (err, result) — первый аргумент всегда ошибка
- Promises — метод .catch()
- Async/await — try/catch
// Плохо: потеря ошибки
async function riskyOperation() {
const data = await fetchData(); // если ошибка — упадёт всё приложение
}
// Хорошо: обработка через try/catch
async function safeOperation() {
try {
const data = await fetchData();
return data;
} catch (error) {
console.error('Ошибка получения данных:', error.message);
throw error; // пробрасываем дальше
}
}
// Для глобального отлова в Node.js
process.on('uncaughtException', (err) => {
console.error('Необработанная ошибка:', err);
process.exit(1); // рекомендуется завершить процесс
});
process.on('unhandledRejection', (reason) => {
console.error('