Собрали 50 самых частых вопросов и задач, которые встречаются на собеседованиях фронтенд-разработчиков. Каждый вопрос — с кратким ответом и примером кода.
Используйте как чеклист: пройдитесь по списку и проверьте, на каждый вопрос можете ответить уверенно. А если где-то застряли — потренируйтесь решать задачи на Девстанции.
var, let и const?var — функциональная область видимости, можно переобъявлять.let — блочная область видимости, нельзя переобъявлять.const — как let, но нельзя переприсваивать.
var x = 1;
var x = 2; // ok
let y = 1;
let y = 2; // SyntaxError
const z = 1;
z = 2; // TypeErrorJavaScript перемещает объявления переменных и функций в начало области видимости.var инициализируется как undefined, let и const остаются в temporal dead zone.
console.log(a); // undefined (var поднята)
var a = 5;
console.log(b); // ReferenceError (let в TDZ)
let b = 10;Строгий режим JavaScript ("use strict").
Запрещает неявное создание глобальных переменных, дублирование параметров, удаление свойств и другие «опасные» действия.
== (с приведением типов), === (без приведения), !=, !==. Всегда используйте === — это предсказуемее.
NaN — «Not a Number». Результат некорректной математической операции. Уникальное свойство: NaN !== NaN.
typeof NaN; // "number"
isNaN("abc"); // true
Number.isNaN("abc"); // false (лучше использовать этот)Оператор для определения типа. Возвращает: "string", "number", "boolean", "undefined", "object" (для null тоже!), "function", "symbol", "bigint".
Falsy: false, 0, "", null, undefined, NaN.
Всё остальное — Truthy (включая [], {}, "0").
Array.isArray(variable) — самый надёжный способ. instanceof Array не работает в разных frame.
Примитивы (string, number, boolean, null, undefined, symbol, bigint) хранятся по значению. Объекты — по ссылке. Копирование объекта создаёт ссылку, а не копию.
let a = [1, 2, 3];
let b = a;
b.push(4);
console.log(a); // [1, 2, 3, 4] — a тоже измениласьШаблонные строки с обратными кавычками. Поддерживают многострочность и интерполяцию через ${}.
const name = "Alice";
const msg = `Hello, ${name}!
How are you?`;8 примитивов: string, number, bigint, boolean, undefined, null, symbol, object. Массивы, функции и даты — это объекты.
== отличается от ===?== приводит типы перед сравнением, === сравнивает и значение, и тип.
0 == ""; // true
0 === ""; // false
null == undefined; // true
null === undefined; // falsestructuredClone(obj) — современный способ. Или рекурсивная функция с JSON.parse(JSON.stringify(obj)) (без Date/RegExp) или ручная рекурсия.
"key" in obj — проверяет и собственные, и унаследованные.obj.hasOwnProperty("key") — только собственные.obj.key !== undefined — простой способ.
Извлечение значений из объектов/массивов в переменные.
const { name, age } = { name: "Alice", age: 25 };
const [first, second] = [1, 2, 3];
const { name: alias } = { name: "Alice" }; // alias = "Alice"Spread (...) раскрывает массив/объект. Rest собирает оставшиеся элементы в массив.
const arr = [1, 2, 3];
const copy = [...arr]; // spread
function sum(...nums) {
// rest
return nums.reduce((a, b) => a + b, 0);
}Возвращают массив ключей, значений или пар [key, value].
const obj = { a: 1, b: 2 };
Object.keys(obj); // ["a", "b"]
Object.values(obj); // [1, 2]
Object.entries(obj); // [["a", 1], ["b", 2]]?.) и Nullish coalescing (??)??. безопасно обращается к свойствам (возвращает undefined вместо ошибки).?? возвращает правый операнд только если левый null или undefined.
const user = { address: { city: "Moscow" } };
user?.address?.zip; // undefined (без ошибки)
null ?? "default"; // "default"
0 ?? "default"; // 0 (не "default", потому что 0 !== null)Declaration поднимается (можно вызвать до объявления). Expression нет.
greet(); // ok (declaration)
function greet() {
console.log("Hi");
}
sayHi(); // ReferenceError (expression)
const sayHi = function () {
console.log("Hi");
};Краткий синтаксис.
Отличия: нет своего arguments, нет своего this (берёт из внешнего контекста), нельзя использовать как конструктор.
const add = (a, b) => a + b;
const square = (x) => x * x; // один аргумент — скобки необязательныФункция «запоминает» переменные из внешней области видимости, даже после завершения внешней функции.
function counter() {
let count = 0;
return {
increment: () => ++count,
getCount: () => count,
};
}
const c = counter();
c.increment(); // 1
c.increment(); // 2
c.getCount(); // 2this?Зависит от того, как вызвана функция:
window (или undefined в strict mode)this из внешнего контекстаcall/apply/bind: явно заданный контекстcall, apply и bind?call — вызывает функцию с указанным this и аргументами через запятую.apply — то же, но аргументы массивом.bind — возвращает новую функцию с фиксированным this.
function greet(greeting) {
console.log(`${greeting}, ${this.name}`);
}
const user = { name: "Alice" };
greet.call(user, "Hello"); // "Hello, Alice"
greet.apply(user, ["Hello"]); // "Hello, Alice"
const bound = greet.bind(user);
bound("Hi"); // "Hi, Alice"Immediately Invoked Function Expression — функция, которая вызывается сразу после объявления.
(function () {
const private = 42;
console.log(private);
})();function debounce(fn, delay) {
let timerId = null;
return function (...args) {
clearTimeout(timerId);
timerId = setTimeout(() => fn.apply(this, args), delay);
};
}function throttle(fn, limit) {
let inThrottle = false;
return function (...args) {
if (!inThrottle) {
fn.apply(this, args);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
}
};
}map, filter, reduce?map — преобразует каждый элемент.filter — оставляет элементы по условию.reduce — накапливает результат.
[1, 2, 3].map((x) => x * 2); // [2, 4, 6]
[1, 2, 3].filter((x) => x > 1); // [2, 3]
[1, 2, 3].reduce((sum, x) => sum + x, 0); // 6find, some, every?find — первый подходящий элемент.some — есть ли хотя бы один подходящий.every — все ли подходят.
[1, 2, 3].find((x) => x > 1); // 2
[1, 2, 3].some((x) => x > 2); // true
[1, 2, 3].every((x) => x > 0); // truesplice отличается от slice?splice изменяет исходный массив (удаляет/добавляет элементы).slice возвращает новый массив без изменения оригинала.
const arr = [1, 2, 3, 4, 5];
arr.splice(1, 2); // arr = [1, 4, 5], вернул [2, 3]
arr.slice(1, 3); // вернул [4, 5], arr не изменилсяconst arr = [1, 2, 3];
arr.reverse(); // изменяет оригинал
[...arr].reverse(); // создаёт копию
arr.toReversed(); // ES2023 — не изменяет оригиналconst arr = [1, 2, 2, 3, 3, 3];
[...new Set(arr)]; // [1, 2, 3]const arr = [1, [2, [3, 4], 5], 6];
arr.flat(Infinity); // [1, 2, 3, 4, 5, 6]
// Ручная реализация:
function flatten(arr) {
return arr.reduce(
(acc, item) => acc.concat(Array.isArray(item) ? flatten(item) : item),
[],
);
}function chunk(arr, size) {
const result = [];
for (let i = 0; i < arr.length; i += size) {
result.push(arr.slice(i, i + size));
}
return result;
}
chunk([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]]const arr = [3, 1, 4, 1, 5, 9];
arr.sort((a, b) => a - b); // [1, 1, 3, 4, 5, 9]
// Без compareFn sort сортирует как строки!Callback — функция, переданная как аргумент другой функции.
Callback hell — вложенные друг в друга колбэки, которые khó читать.
getData(function (a) {
getMoreData(a, function (b) {
getEvenMoreData(b, function (c) {
console.log(c);
});
});
});Объект, представляющий результат асинхронной операции.
Три состояния: pending, fulfilled, rejected.
const promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("done"), 1000);
});
promise.then((result) => console.log(result));then, catch, finally?then — обрабатывает успешный результат.catch — обрабатывает ошибку.finally — выполняется в любом случае.
fetch("/api/data")
.then((res) => res.json())
.catch((err) => console.error(err))
.finally(() => hideSpinner());Promise.all, Promise.race, Promise.allSettled?all — ждёт все промисы, реджектится при первой ошибке.race — первый завершившийся (успех или ошибка).allSettled — ждёт все, возвращает статус каждого.
Promise.all([p1, p2, p3]); // [r1, r2, r3]
Promise.race([p1, p2, p3]); // r1 (если p1 быстрее)
Promise.allSettled([p1, p2, p3]); // [{status: "fulfilled", ...}, ...]async — функция всегда возвращает промис.await — приостанавливает выполнение до завершения промиса.
async function getData() {
try {
const res = await fetch("/api/data");
const data = await res.json();
return data;
} catch (err) {
console.error(err);
}
}Механизм обработки асинхронных операций в JavaScript. Макрозадачи (setTimeout, fetch) выполняются по очереди. Микрозадачи (Promise, queueMicrotask) — приоритетнее.
console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
console.log("4");
// Вывод: 1, 4, 3, 2async function sequence(promises) {
const results = [];
for (const p of promises) {
results.push(await p);
}
return results;
}// Способ 1: try/catch
async function foo() {
try {
await riskyOperation();
} catch (err) {
handleError(err);
}
}
// Способ 2: .catch() на промисе
async function bar() {
await riskyOperation().catch(handleError);
}Обработчик вешается на родителя, а определение целевого элемента через event.target. Эффективнее, чем вешать обработчик на каждый дочерний элемент.
document.querySelector("ul").addEventListener("click", (e) => {
if (e.target.tagName === "LI") {
console.log(e.target.textContent);
}
});stopPropagation отличается от preventDefault?stopPropagation останавливает всплытие/погружение события.preventDefault отменяет действие браузера по умолчанию (переход по ссылке, отправку формы).
Cross-Origin Resource Sharing — механизм безопасности браузера. Блокирует запросы с другого домена, если сервер не добавил заголовок Access-Control-Allow-Origin.
Хранит данные в браузере без времени жизни. Данные — строки. Максимум ~5-10 МБ. Доступны между вкладками.
localStorage.setItem("key", JSON.stringify({ a: 1 }));
const data = JSON.parse(localStorage.getItem("key"));Proxy перехватывает операции над объектом (чтение, запись, удаление). Reflect предоставляет стандартные методы для этих перехватов.
const handler = {
get(target, prop) {
return prop in target ? target[prop] : "default";
},
};
const proxy = new Proxy({ name: "Alice" }, handler);
proxy.name; // "Alice"
proxy.age; // "default"Уникальный и неизменяемый примитив. Часто используется как ключ свойства.
const id = Symbol("id");
const obj = { [id]: 42 };
obj[id]; // 42Итеративный объект имеет метод next() с {value, done}. Generator — функция с yield, возвращающая итератор.
function* range(start, end) {
for (let i = start; i <= end; i++) {
yield i;
}
}
const gen = range(1, 3);
gen.next(); // { value: 1, done: false }
gen.next(); // { value: 2, done: false }
gen.next(); // { value: 3, done: false }
gen.next(); // { value: undefined, done: true }const original = { a: 1, b: { c: 2 } };
const copy1 = { ...original };
const copy2 = Object.assign({}, original);
const copy3 = structuredClone(original); // глубокая копия
// spread и Object.assign - поверхностная копия (вложенные объекты по ссылке)Этот чеклист — основа. Но на собеседовании спрашивают не только теорию — просят написать код, объяснить решение, обсудить архитектуру.
Попробуйте решить задачи на Девстанции — здесь есть десятки задач разной сложности с автоматической проверкой и AI-ревью кода. Это лучший способ закрепить знания на практике.
А если готовитесь к конкретному собеседованию — посмотрите план подготовки за неделю и 10 задач с разбором решений.