| layout | ../../layouts/Layout.astro |
|---|---|
| title | JavaScript |
| description | Вопросы и ответы |
| category | Frontend |
| kind | questions |
| order | 70 |
Что такое shallow copy и deep copy?
|
Shallow copy создает новый верхний объект, но вложенные объекты остаются общими. Deep copy рекурсивно создает независимые вложенные значения. Полное копирование может быть дорогим и не всегда имеет смысл для immutable updates. |
Чем примитивы отличаются от объектов?
|
Примитивы — |
Что значит "передача по значению" в JavaScript?
|
При вызове функции параметр получает копию значения аргумента. Для примитива копируется сам примитив, для объекта — значение ссылки на объект. Переназначение параметра не меняет внешнюю переменную. |
Передаются ли объекты по ссылке в JavaScript?
|
Точнее говорить: объекты тоже передаются по значению, но этим значением является ссылка на объект. Функция и вызывающий код получают две копии ссылки на один объект. Поэтому мутация объекта видна снаружи, а переназначение параметра — нет. |
Чем отличается ссылка на объект от самого объекта?
|
Объект содержит состояние в памяти, а ссылка позволяет найти конкретный объект. Несколько переменных могут содержать ссылки на один объект. Изменение через любую из них будет наблюдаться через остальные. |
Почему изменение свойства объекта внутри функции видно снаружи?
|
Скопированная ссылка параметра указывает на тот же объект, что и внешняя переменная. Запись свойства меняет этот общий объект. Новый объект при этом не создается. |
Почему переназначение параметра внутри функции не меняет внешнюю переменную?
|
Параметр — локальная переменная функции. Присваивание заменяет только локальную копию ссылки, а внешняя переменная продолжает указывать на прежний объект. function changeUser(user) {
user.name = 'Alex';
}
function replaceUser(user) {
user = {name: 'Bob'};
}
const user = {name: 'Max'};
changeUser(user);
console.log(user.name); // Alex
replaceUser(user);
console.log(user.name); // Alex |
Как работает сравнение объектов через ===?
|
|
Чем отличаются spread, Object.assign и structuredClone?
|
Spread и |
Какие ограничения есть у JSON.parse(JSON.stringify(...)) для deep copy?
|
Метод теряет |
Что такое область видимости в JS?
|
Область видимости в JavaScript — это правило, которое определяет, где переменная, функция или класс доступны в коде. function test() {
const name = 'Max';
console.log(name); // доступна
}
console.log(name); // ошибка: name не видна снаружиОсновные виды scope в JS
Доступно везде в файле/программе: const appName = 'My App';
function log() {
console.log(appName);
}
var виден внутри всей функции: function test() {
var x = 1;
if (true) {
var y = 2;
}
console.log(y); // 2
}
let и const видны только внутри блока {}: if (true) {
const x = 1;
let y = 2;
}
console.log(x); // ошибка
console.log(y); // ошибка
В ES-модулях переменные не попадают в global scope: const value = 123;
export {value}; |
Какие типы данных есть в JavaScript?
|
В JavaScript есть семь примитивных типов: Все остальные значения относятся к типу // Примитивные
string; // "hello"
number; // 123, 3.14, NaN, Infinity
bigint; // 123n
boolean; // true / false
undefined; // значение не задано
null; // пустое значение
symbol; // уникальный идентификатор
// Ссылочный тип
object; // {}, [], function, Date, Map, Set и т.д.Важно: typeof null; // "object" — старая странность JS
typeof []; // "object"
typeof function () {}; // "function"typeof Symbol('id'); // "symbol" |
В чем разница между call и apply, bind в JS?
|
call и apply делают одно и то же: вызывают функцию с явно заданным this. call Аргументы передаются через запятую: function greet(city, age) {
console.log(`${this.name}, ${city}, ${age}`);
}
const user = {name: 'Max'};
greet.call(user, 'Moscow', 32);
// Max, Moscow, 32
// fn.call(thisArg, arg1, arg2, arg3);function greet(city, age) {
console.log(`${this.name}, ${city}, ${age}`);
}
const user = {name: 'Max'};
greet.apply(user, ['Moscow', 32]);
// Max, Moscow, 32bind тоже работает с this, но не вызывает функцию сразу. Он создает новую функцию, у которой this уже заранее привязан. function greet(city) {
console.log(`${this.name} from ${city}`);
}
const user = {name: 'Max'};call — вызывает сразу. apply — вызывает сразу, но аргументы массивом. bind — НЕ вызывает сразу const boundGreet = greet.bind(user);
boundGreet('Moscow');
// Max from Moscow |
В чем отличие нативных (Native) объектов от хост-объектов (Host objects)?
|
Нативные объекты — часть спецификации языка. Они доступны нам вне зависимости от того, на каком клиенте исполняется наш код. Примеры: Array, Date и Math. Полный список нативных объектов. var users = Array(); // Array — нативный объектВстроенные (Built-in): Array, Date, Math, String, Promise, Object. Пользовательские: Объекты, создаваемые вами через new Object(), литералы {} или классы. Контекстные: Объект globalThis (или window в браузере, global в Node.js), Math и JSON. Хост-объекты (Host objects) Это объекты, предоставляемые средой выполнения (окружением), в которой запущен JavaScript (браузер, сервер Node.js и т.д.). Они не являются частью самого языка, зависят от платформы и могут различаться. В браузере: window, document, location, history, XMLHTTPRequest, fetch, элементы DOM, localStorage. В Node.js: Объекты для работы с файловой системой (fs), процессами (process), операционной системой (os). |
Что такое Object.fromEntries?
|
const entries = [
['name', 'Max'],
['role', 'frontend'],
];
const user = Object.fromEntries(entries);
// { name: "Max", role: "frontend" }Пример фильтрации свойств: const params = {
search: 'angular',
page: 1,
empty: undefined,
};
const cleaned = Object.fromEntries(Object.entries(params).filter(([, value]) => value !== undefined));
// { search: "angular", page: 1 } |
Что такое Array.prototype.reduce?
|
const total = [10, 20, 30].reduce((sum, value) => sum + value, 0);
// 60Initial value стоит задавать явно, особенно если массив может быть пустым: const byYear = operations.reduce<Record<string, typeof operations>>((groups, operation) => {
const year = operation.date.slice(0, 4);
groups[year] ??= [];
groups[year].push(operation);
return groups;
}, {});Не стоит использовать |
Что такое sparse array?
|
Sparse array, разреженный массив, содержит пустые слоты, в которых нет свойства с соответствующим индексом. Это не то же
самое, что явное значение const sparse = [1, , 3];
0 in sparse; // true
1 in sparse; // false
sparse.length; // 3Методы ведут себя по-разному: |
Что такое ISO-формат даты?
|
ISO 8601 - распространенный стандарт записи даты и времени. В JavaScript часто встречается формат
const iso = '2026-06-20T10:30:00.000Z';
|
Что такое Object.groupBy и когда его использовать?
|
const operations = [
{date: '2017-07-31', amount: 5422},
{date: '2018-03-31', amount: 5654},
{date: '2017-08-31', amount: 5451},
];
const byYear = Object.groupBy(operations, ({date}) => date.slice(0, 4));
// {
// "2017": [...],
// "2018": [...]
// }
|
Чем Object.groupBy отличается от Map.groupBy?
|
const active = {label: 'active'};
const archived = {label: 'archived'};
const users = [
{name: 'Max', status: active},
{name: 'Anna', status: archived},
{name: 'Kate', status: active},
];
const grouped = Map.groupBy(users, ({status}) => status);
grouped.get(active);
// [{ name: "Max", ... }, { name: "Kate", ... }]Выбор зависит от того, какие ключи нужны и будет ли результат дальше использоваться как объект или |
Что делает Object.hasOwn?
|
const user = Object.create({role: 'admin'}) as {
name?: string;
role: string;
};
user.name = 'Max';
Object.hasOwn(user, 'name'); // true
Object.hasOwn(user, 'role'); // false
'role' in user; // trueВ отличие от |
Чем Object.hasOwn отличается от оператора in?
|
|
Чем Object.entries отличается от Object.fromEntries?
|
const user = {name: 'Max', role: 'frontend'};
const entries = Object.entries(user);
const copy = Object.fromEntries(entries);Symbols не попадают в |
Что возвращает Object.keys и в каком порядке?
|
Integer-like ключи идут по возрастанию, остальные строковые ключи - в порядке добавления: const value = {10: 'ten', 2: 'two', name: 'Max'};
Object.keys(value); // ["2", "10", "name"]Порядок определен спецификацией, но бизнес-логику сортировки лучше выражать явно, а не связывать со способом хранения объекта. |
Чем flat отличается от flatMap?
|
const nested = [[1, 2], [3]];
nested.flat(); // [1, 2, 3]
const users = [
{name: 'Max', roles: ['admin', 'editor']},
{name: 'Anna', roles: ['viewer']},
];
const roles = users.flatMap(({roles}) => roles);
// ["admin", "editor", "viewer"] |
Когда использовать flatMap вместо map().flat()?
|
const values = [1, -1, 2];
const positive = values.flatMap((value) => (value > 0 ? [value] : []));
// [1, 2]
|
Чем toSorted отличается от sort?
|
const numbers = [10, 2, 30];
const sorted = numbers.toSorted((first, second) => first - second);
console.log(numbers); // [10, 2, 30]
console.log(sorted); // [2, 10, 30]
const numbers = [10, 2, 30];
numbers.sort((first, second) => first - second);
console.log(numbers); // [2, 10, 30]Для чисел нужен comparator, иначе значения сортируются как строки. |
Чем reverse отличается от toReversed?
|
const source = [1, 2, 3];
const reversed = source.toReversed();
console.log(source); // [1, 2, 3]
console.log(reversed); // [3, 2, 1] |
Чем splice отличается от toSpliced?
|
const source = ['a', 'b', 'c'];
const updated = source.toSpliced(1, 1, 'x');
console.log(source); // ["a", "b", "c"]
console.log(updated); // ["a", "x", "c"] |
Что делает array.with?
|
const source = ['draft', 'review', 'done'];
const updated = source.with(1, 'approved');
console.log(source); // ["draft", "review", "done"]
console.log(updated); // ["draft", "approved", "done"]Недопустимый индекс приводит к |
Почему immutable-методы массивов полезны в Angular?
|
readonly users = signal<ReadonlyArray<User>>([]);
sortByName(): void {
this.users.update((users) =>
users.toSorted((first, second) => first.name.localeCompare(second.name)),
);
}Мутация массива на месте может не создать ожидаемого реактивного обновления и усложняет сравнение предыдущего и нового состояния. |
Какие базовые API есть у Date?
|
const now = new Date();
now.getFullYear();
now.getMonth(); // 0-11
now.getDate(); // День месяца
Date.now(); // Текущий timestamp в миллисекундах
now.getTime(); // Timestamp конкретной даты
|
Что делает Date.prototype.toISOString?
|
const date = new Date('2026-06-20T10:30:00+03:00');
date.toISOString();
// "2026-06-20T07:30:00.000Z"Метод удобен для API, логов, сериализации и приведения моментов времени к единому формату. |
Какие ошибки часто допускают при работе с Date?
const date = new Date();
date.setDate(date.getDate() + 1); // Мутирует исходный объектДля более сложной работы с датами развивается |
Как сравнивать даты в JavaScript?
|
Моменты времени удобно сравнивать по timestamp через const first = new Date('2026-06-20T10:00:00.000Z');
const second = new Date('2026-06-20T12:00:00.000Z');
first.getTime() < second.getTime(); // trueДля API моменты времени обычно передают в ISO/UTC. Если значение является календарной датой без времени, например днем
рождения, его часто безопаснее хранить отдельной строкой |
Что такое Promise.try?
|
const result = await Promise.try(parseConfig, rawConfig);API удобно на границе, где callback может быть синхронным или асинхронным. Это новый стандартный метод, поэтому перед использованием нужно проверить поддержку целевых browsers и runtime. |
Чем Promise.all отличается от Promise.allSettled?
|
const results = await Promise.allSettled([loadProfile(), loadRecommendations()]);
const successful = results.filter((result): result is PromiseFulfilledResult<unknown> => result.status === 'fulfilled');
|
Что произойдет при ошибке внутри Promise.all?
|
Итоговый promise отклонится с причиной первого обнаруженного rejection. Остальные запущенные операции автоматически не отменяются и могут продолжить работу. Если допустим частичный результат, используют |
Когда использовать Promise.race?
|
Важно: проигравшие операции автоматически не отменяются. |
Чем Promise.any отличается от Promise.race?
|
|
Что такое Promise.withResolvers?
|
const {promise, resolve, reject} = Promise.withResolvers<string>();
button.addEventListener('click', () => resolve('confirmed'), {once: true});
const result = await promise;Метод удобен при адаптации callback/event API, но внешнее управление promise усложняет жизненный цикл. Для обычной
последовательной логики чаще проще |
Что такое URL и URLSearchParams?
|
const url = new URL('/users', 'https://example.com');
url.searchParams.set('page', '2');
url.searchParams.set('search', 'Angular & RxJS');
url.toString();
// "https://example.com/users?page=2&search=Angular+%26+RxJS"Это безопаснее и понятнее ручной конкатенации query string. |
Как добавлять, изменять и удалять query parameters?
const params = new URLSearchParams('page=1');
params.set('page', '2');
params.append('tag', 'angular');
params.delete('page');
params.toString(); // "tag=angular"
|
Чем URLSearchParams.append отличается от set?
|
const params = new URLSearchParams();
params.append('tag', 'angular');
params.append('tag', 'rxjs');
params.get('tag'); // "angular"
params.getAll('tag'); // ["angular", "rxjs"]Для multi-value параметров используют |
Что такое AbortController и AbortSignal?
|
const controller = new AbortController();
const request = fetch('/api/users', {
signal: controller.signal,
});
controller.abort();
await request; // Rejection с AbortErrorОдин signal можно передать нескольким связанным операциям. |
Как сделать timeout для fetch?
|
Современный вариант использует const response = await fetch('/api/users', {
signal: AbortSignal.timeout(5_000),
});Если нужен ручной контроль, создают const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5_000);
try {
return await fetch('/api/users', {signal: controller.signal});
} finally {
clearTimeout(timeoutId);
} |
Чем отмена запроса отличается от игнорирования результата?
|
Игнорирование результата не останавливает сетевую работу и обработку ответа. Настоящая отмена через В Angular это встречается в autocomplete, навигации между страницами и уничтожении компонентов. |
Что такое REST?
|
REST (Representational State Transfer) — это набор правил и принципов для построения взаимодействия между программами (клиентом и сервером) через интернет. Обычно клиент (например, браузер или мобильное приложение) запрашивает данные у сервера, а тот их возвращает, чаще всего по протоколу HTTP. Ключевые принципы REST Клиент-серверная модель:
|
Что было до REST и после?
|
REST (Representational State Transfer) — это набор правил и принципов для построения взаимодействия между программами (клиентом и сервером) через интернет. Обычно клиент (например, браузер или мобильное приложение) запрашивает данные у сервера, а тот их возвращает, чаще всего по протоколу HTTP. Ключевые принципы REST Клиент-серверная модель:
Идея: клиент вызывает удаленную функцию как обычную функцию. userService.getUser(123);На уровне сети это превращалось в запрос к серверу. Минус: клиент часто сильно завязан на серверные методы. То есть API выглядит как набор команд.
SOAP — более формальный XML-based подход. <soap:Envelope>
<soap:Body>
<GetUser>
<UserId>123</UserId>
</GetUser>
</soap:Body>
</soap:Envelope>Особенности:
Минус: тяжеловесно, много boilerplate.
REST стал популярным как более простой HTTP-подход. call getUser(123)Появляется ресурс: GET / users / 123;Вместо: call deleteUser(123)REST-стиль: Главная идея REST: API строится вокруг ресурсов, а HTTP-методы описывают действие. REST никуда не исчез. Он до сих пор основной стандарт для обычных web API. Но рядом появились другие подходы.
Идея: клиент сам говорит, какие поля ему нужны. query {
user(id: 123) {
name
avatar
posts {
title
}
}
}Плюсы:
Минусы:
Хорошо подходит, когда UI сложный и REST начинает плодить много endpoint-ов.
Идея: быстрые типизированные контракты между сервисами. Обычно используется не browser ↔ backend, а backend ↔ backend. service UserService {
rpc GetUser (GetUserRequest) returns (User);
}Плюсы:
Минусы:
|
Что такое hydration в JavaScript?
|
Hydration — это процесс, при котором JavaScript подключает интерактивное поведение к уже существующей HTML-разметке, обычно полученной с сервера через SSR или prerender. Браузер сначала показывает готовый HTML, а затем клиентский код находит нужные DOM-узлы, восстанавливает состояние и навешивает обработчики событий. Упрощенный пример: <button
id="counter"
data-count="0"
>
0
</button>
<script type="module">
const button = document.querySelector('#counter');
if (button instanceof HTMLButtonElement) {
let count = Number(button.dataset.count ?? 0);
button.addEventListener('click', () => {
count += 1;
button.dataset.count = String(count);
button.textContent = String(count);
});
}
</script>До выполнения скрипта кнопка уже видна пользователю, но еще не интерактивна. После hydration обработчик Главное требование — клиентский код должен ожидать такую же начальную разметку, какую отдал сервер. Если сервер
отрендерил |
Чем отличается queueMicrotask от setTimeout?
console.log('1');
setTimeout(() => console.log('setTimeout'), 0);
queueMicrotask(() => console.log('queueMicrotask'));
console.log('2');Вывод: 1;
2;
queueMicrotask;
setTimeout; |
Что такое Event loop?
|
JavaScript в браузере выполняется в основном в одном потоке. Поэтому ему нужен диспетчер, который по очереди обрабатывает:
console.log('1');
setTimeout(() => {
console.log('2');
}, 0);
Promise.resolve().then(() => {
console.log('3');
});
queueMicrotask(() => {
console.log('4');
});
console.log('5');1;
5;
3;
4;
2; |
Что такое this и расскажите про область видимости?
|
Область видимости определяет, где доступна переменная. В JavaScript есть глобальная, модульная, функциональная и блочная
область видимости.
class Counter {
count = 0;
increment = (): void => {
this.count += 1;
};
}Нельзя определять |
Что такое Promise и для чего используется в JS?
|
fetch('/api/users')
.then((response) => response.json())
.catch((error: unknown) => handleError(error))
.finally(() => hideLoader());Обработчики Для параллельной работы есть |
Что такое макро и микро задачи в JS?
|
Термин task часто неформально называют macrotask.
После завершения текущей task движок полностью очищает очередь microtasks и только затем может выполнить рендер и перейти к следующей task. Если microtasks непрерывно добавляют новые microtasks, они могут задержать рендер и обработку событий. Поэтому тяжелую
работу нельзя бесконечно дробить только через Promise или |
Что такое класс и интерфейс?
|
Класс описывает создание объектов, их состояние и поведение. Класс существует во время выполнения JavaScript. Интерфейс TypeScript описывает контракт формы значения и используется только при проверке типов. После компиляции интерфейс исчезает. interface UserRepository {
findById(id: string): Promise<User | null>;
}
class HttpUserRepository implements UserRepository {
async findById(id: string): Promise<User | null> {
return loadUser(id);
}
}Интерфейс нельзя напрямую использовать как Angular DI-токен, потому что его нет в runtime. Для DI используют класс,
|
Что такое конструктор класса?
|
Конструктор — специальный метод, который выполняется при создании экземпляра через class User {
constructor(
readonly id: string,
readonly name: string,
) {}
}В производном классе до обращения к В Angular конструктор класса не является lifecycle hook. Для компонентов он должен оставаться простым: DI и базовая инициализация выполняются в конструкторе, а логика, зависящая от входных данных, размещается в соответствующем lifecycle hook или реактивной модели. |
В чем отличие var от const, let?
const user = {name: 'Ann'};
user.name = 'Kate'; // ДопустимоПо умолчанию используют |
Как устроено прототипное наследование в JavaScript?
|
Каждый обычный объект может иметь внутреннюю ссылку const animal = {moves: true};
const dog = Object.create(animal);
dog.barks = true;
console.log(dog.moves); // Найдено в прототипеСинтаксис Слишком глубокие и изменяемые цепочки усложняют код. В прикладной разработке часто предпочитают композицию небольших объектов наследованию. |
Почему Set.has часто лучше Array.includes для частых проверок?
|
const visibleIds = new Set(filters.visibleIds);
const visibleRows = rows.filter((row) => visibleIds.has(row.id));Для одного короткого списка |
Когда Map лучше обычного объекта?
|
Обычный объект удобен для JSON-like данных и фиксированной формы: const user = {
id: '42',
name: 'Ann',
};
const usersById = new Map(users.map((user) => [user.id, user]));
const currentUser = usersById.get(currentUserId);В Angular-состоянии часто хранят и |
Какие ошибки часто бывают в comparator для sort?
|
Comparator должен возвращать отрицательное число, ноль или положительное число и быть согласованным. Частые ошибки: возвращать boolean, мутировать элементы, зависеть от внешнего меняющегося состояния, забывать числовой comparator или не обрабатывать равенство. const wrong = prices.toSorted((first, second) => (first.price > second.price ? 1 : -1));
const correct = prices.toSorted((first, second) => first.price - second.price);Для строк лучше использовать |
Почему string.length в JavaScript не всегда равен количеству видимых символов?
|
'😄'.length; // 2
Array.from('😄').length; // 1 code pointДаже code point не всегда равен видимому символу: один grapheme cluster может состоять из нескольких code points.
Поэтому обрезка пользовательского имени, textarea limit или preview текста должны учитывать Unicode, если продукт
работает с emoji и разными языками. Для пользовательских символов можно использовать |
Почему 0.1 + 0.2 !== 0.3?
|
JavaScript 0.1 + 0.2; // 0.30000000000000004Для отображения используют округление, например const totalCents = 10 + 20;
const formatted = new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'RUB',
}).format(totalCents / 100); |
Какие ограничения есть у bitwise operators в JavaScript?
|
Bitwise operators приводят значения к 32-bit signed integer, кроме Math.pow(2, 40) | 0; // 0Битовые операции уместны для flags, binary protocols, canvas/image processing и TypedArray. Для обычного UI-кода
читаемая структура вроде |
Как устроена память в JavaScript (memory heap, memory stack)?
|
Упрощенная модель состоит из call stack и heap:
Сборщик мусора освобождает объекты, которые больше недостижимы от корней приложения. Основная идея современных сборщиков мусора — mark and sweep. Типичные причины утечек: забытые подписки и обработчики, бесконечно растущий кеш, таймеры, замыкания и ссылки на
удаленные DOM-узлы. В Angular для подписок можно использовать |
Что такое call-stack, task-queue (приведите примеры работы)?
|
Call stack хранит активные вызовы функций. JavaScript выполняет верхний frame стека и снимает его после возврата из функции. Task queue содержит готовые к выполнению задачи: таймеры, DOM-события и другие callbacks. Event loop передает следующую задачу в стек, когда стек пуст и обработаны microtasks. console.log('A');
setTimeout(() => console.log('B'), 0);
Promise.resolve().then(() => console.log('C'));
console.log('D');Порядок вывода: |
Почему event loop не спасает от плохой алгоритмической сложности?
|
Event loop умеет чередовать задачи, но синхронный JavaScript все равно выполняется на main thread до завершения текущей task. Если обработчик клика сортирует, фильтрует и рендерит огромный список за сотни миллисекунд, браузер не сможет обработать input и paint посередине этой работы. Решения зависят от причины: улучшить алгоритм, заранее построить индекс через |
Что такое event delegation?
|
Event delegation — это прием, при котором обработчик события ставят на общего родителя, а не на каждый дочерний элемент.
Событие всплывает вверх по DOM, а обработчик определяет исходный элемент через list.addEventListener('click', (event) => {
const button = event.target.closest('button[data-id]');
if (!button || !list.contains(button)) {
return;
}
selectItem(button.dataset.id);
}); |
Как работает prototypal inheritance?
|
Объект имеет internal prototype, по которому JavaScript ищет свойство, если его нет на самом объекте. Этот поиск идет по
prototype chain до |
Что такое closure и когда она полезна?
|
Closure возникает, когда функция сохраняет доступ к переменным внешней lexical scope после завершения внешней функции. Она полезна для callbacks, factories, инкапсуляции состояния и partial application. Нужно помнить, что closure удерживает ссылки на значения и может продлить их жизнь в памяти. |
Что такое hoisting?
|
Hoisting — поведение, при котором declarations обрабатываются до выполнения кода в scope. Function declarations доступны
до объявления, |
Что такое type coercion?
|
Type coercion — неявное или явное преобразование значения к другому типу. JavaScript делает это в арифметике, сравнениях, string concatenation и boolean context. Неявные правила бывают неожиданными, поэтому в application code лучше явно преобразовывать данные на границах. |
Что такое event bubbling?
|
Bubbling — фаза события, когда оно идет от target вверх по ancestors до document/window. На этом основан event
delegation. Обработчик должен проверять |
Что такое event capturing?
|
Capturing — фаза события до target, когда событие идет сверху вниз от window/document к целевому элементу. Обработчик
подключают через |
Что такое strict mode?
|
Strict mode включает более строгие правила JavaScript: запрещает часть неявных globals, меняет |
Что такое mutable и immutable objects?
|
Mutable object можно изменить на месте, сохранив ту же ссылку. Immutable-подход создает новое значение при изменении. В Angular immutability помогает predictable state, signals, OnPush/change detection и простое сравнение предыдущего и нового состояния. |
Что такое higher-order function?
|
Higher-order function принимает функцию как аргумент или возвращает функцию. Примеры: |
Что такое destructuring?
|
Destructuring извлекает значения из массива или объекта в переменные по структуре. Он удобен для параметров функции, DTO mapping и работы с tuples. Важно помнить про defaults и то, что destructuring не делает deep clone. const user = {name: 'Max', role: 'admin'};
const {name, role = 'user'} = user; |
Что такое template literal?
|
Template literal — строка в backticks с interpolation через |
Что такое currying?
|
Currying превращает функцию от нескольких аргументов в цепочку функций по одному аргументу. Это удобно для частичного применения, factories и композиции. В обычном application code его стоит использовать там, где он реально делает вызовы понятнее, а не ради функционального стиля. |
Что такое Promise?
|
Promise представляет будущий результат asynchronous operation: pending, fulfilled или rejected. Он позволяет строить
цепочки через |
Зачем команде нужны JavaScript principles?
|
JavaScript principles фиксируют базовые договоренности: как писать modules, работать с async code, обрабатывать ошибки, именовать функции, избегать side effects и организовывать shared utilities. Это уменьшает хаос в больших проектах и делает поведение кода предсказуемым для review, testing и production debugging. |
Когда проекту нужны polyfills или shims?
|
Polyfills нужны, когда приложение использует JavaScript-возможность или Web API, которых нет в целевых браузерах. Их список должен зависеть от browser support policy и реальной аналитики, а не добавляться на всякий случай. Лишние polyfills увеличивают bundle size и могут ухудшать startup performance. |
Почему third-party scripts нужно контролировать?
|
Third-party scripts влияют на performance, security, privacy и стабильность приложения. Analytics, maps, chats, A/B tools и widgets часто грузятся вне основного lifecycle приложения. Их нужно инвентаризировать, ограничивать, мониторить и по возможности загружать лениво или после согласия пользователя. |
Зачем команде договариваться о JavaScript commenting?
|
Комментарии должны объяснять причину решения, ограничение, invariant или важный edge case, а не очевидный синтаксис. Если код требует много комментариев, возможно, его стоит упростить. Для публичных API, shared utilities и сложной бизнес-логики полезны JSDoc или TSDoc, если они поддерживаются командным workflow. |
Что значит следовать JavaScript patterns?
|
Это значит использовать узнаваемые подходы к организации кода: modules, factories, observers, adapters, dependency injection, immutable updates и другие patterns. Pattern полезен, когда упрощает понимание и расширение кода. Если он добавляет только церемонию и не решает реальную проблему, maintainability ухудшается. |
Как работает this в JavaScript?
|
|
Как arrow function меняет поведение this?
|
Arrow function не имеет собственного |
Чем null, undefined и undeclared variable отличаются?
|
|
Какие способы итерации по массивам и объектам есть в JavaScript?
|
Для массивов используют |
Чем forEach() отличается от map()?
|
|
Когда использовать anonymous function?
|
Anonymous function уместна для короткого одноразового callback, когда имя не добавляет смысла. Для сложной логики, recursion, stack traces и тестирования лучше именованная функция. В review хороший критерий простой: понятно ли действие из окружающего кода без отдельного имени. |
Что происходит при вызове функции с new?
|
|
Чем function declaration отличается от function expression?
|
Function declaration поднимается целиком и доступна до места объявления. Function expression создается во время
выполнения выражения; если она присвоена |
Чем feature detection отличается от проверки user agent?
|
Feature detection проверяет наличие нужной возможности, например через |
Почему == может быть опасен?
|
|
Чем HTML attribute отличается от DOM property?
|
Attribute — текстовое значение в HTML-разметке, а DOM property — свойство объекта в памяти браузера. Некоторые значения
отражаются друг в друга, но не всегда одинаково: |
Почему не стоит расширять built-in prototypes?
|
Добавление методов в |
Какие плюсы и минусы у языков, которые компилируются в JavaScript?
|
TypeScript, Elm, ClojureScript и другие языки могут дать типы, иные модели архитектуры и tooling. Цена — build step, source maps, зависимость от компилятора и необходимость понимать итоговый JavaScript при debugging. Для Angular TypeScript является стандартным выбором, но runtime-поведение все равно определяется JavaScript и браузером. |
Какие плюсы и минусы у immutability?
|
Плюсы: проще reasoning, undo/redo, memoization, change detection и тестирование. Минусы: дополнительные allocations, копирование больших структур и необходимость дисциплины при вложенных обновлениях. На практике immutable updates часто сочетают с нормализованными данными и точечным controlled mutation внутри локального алгоритма. |
Чем synchronous и asynchronous functions отличаются?
|
Synchronous function выполняется до завершения в текущей task и блокирует следующий код. Asynchronous function планирует продолжение через Promise, callback, timer, event или stream, позволяя event loop выполнять другие задачи. Асинхронность не означает parallelism: CPU-heavy работа все равно может блокировать main thread. |
Можно ли изменить объект, объявленный через const?
|
|
Чем ES6 class отличается от ES5 constructor function?
|
ES6 |
Чем spread отличается от rest?
|
Spread раскладывает iterable или свойства объекта в месте вызова или создания значения: |
Как делить код между файлами?
|
В modern JavaScript используют ES modules: |
Для чего нужны static class members?
|
Static members принадлежат class constructor, а не instance. Их используют для factories, constants, cache, utility methods и metadata, которая относится к типу в целом. Важно не превращать static state в скрытое глобальное состояние, которое мешает тестам и SSR. |
Чем while отличается от do while?
|
|
Чем event.target отличается от event.currentTarget?
|
|
Чем preventDefault() отличается от stopPropagation()?
|
|
Как выбирать JavaScript framework или library для проекта?
|
Framework или library выбирают по задаче, экосистеме, поддержке команды, долгосрочной maintainability, performance и стоимости миграции. Хороший ответ сравнивает не только developer experience, но и риски: lock-in, bundle size, тестируемость, accessibility, SSR и доступность специалистов. |
Чем call stack отличается от task queue?
|
Call stack хранит текущую цепочку синхронных вызовов. Task queue содержит задачи, которые event loop возьмет позже: timers, events, network callbacks. Promise callbacks попадают в microtask queue и выполняются после текущего stack перед следующей macrotask. |
Что вернет 10 + '20' и почему?
|
Выражение вернет строку |
Почему 0.1 + 0.2 === 0.3 возвращает false?
|
Десятичные дроби |
Реализуйте add(2, 5) и add(2)(5).
|
Функция может проверить, передан ли второй аргумент, и вернуть либо сумму, либо функцию, ожидающую второй операнд. function add(first, second) {
if (second !== undefined) {
return first + second;
}
return (next) => first + next;
}Такой пример проверяет closures, arity и аккуратное отношение к falsy values. |
Как развернуть строку через split, reverse, join и какие ограничения у такого подхода?
|
Базовый вариант: const reversed = 'hello'.split('').reverse().join('');Он работает для простых BMP-символов, но может ломать emoji, surrogate pairs и combining marks. Для пользовательского
текста лучше учитывать Unicode grapheme clusters, например через |
Что делает выражение window.foo || (window.foo = 'bar')?
|
Если |
Почему переменная из IIFE недоступна снаружи?
|
IIFE создает собственную function scope, поэтому переменные внутри нее не попадают во внешнюю область. До ES modules и
(function () {
const secret = 42;
})(); |
Что будет с foo.length после двух push?
|
Если |
Что произойдет в выражении foo.x = foo = {n: 2}?
|
Левая ссылка |
В каком порядке выведутся console.log, setTimeout и Promise.then?
console.log('one');
setTimeout(() => {
console.log('two');
}, 0);
Promise.resolve().then(() => {
console.log('three');
});
console.log('four');Порядок: |
Чем отличаются promise chain с return, без return, с вызовом функции и с передачей функции?
|
Если |
Почему var a = b = 3 может создать implicit global?
|
Выражение читается как |
Почему return и объект на следующей строке могут сломаться из-за ASI?
|
Automatic Semicolon Insertion вставит semicolon после function getUser() {
return {
name: 'Max',
};
} |
Реализуйте duplicate([1, 2, 3]), чтобы получить [1, 2, 3, 1, 2, 3].
function duplicate(items) {
return [...items, ...items];
}Это shallow copy: вложенные объекты не клонируются, а остаются теми же ссылками. |
Реализуйте FizzBuzz до 100.
for (let value = 1; value <= 100; value += 1) {
const isFizz = value % 3 === 0;
const isBuzz = value % 5 === 0;
if (isFizz && isBuzz) {
console.log('fizzbuzz');
} else if (isFizz) {
console.log('fizz');
} else if (isBuzz) {
console.log('buzz');
} else {
console.log(value);
}
}Задача проверяет порядок условий, modulo и читаемую структуру ветвлений. |
Что вернут выражения 'hello' || 'world' и 'foo' && 'bar'?
|
|
Напишите IIFE и объясните, зачем она раньше использовалась.
(function () {
const localValue = 'hidden';
window.appName = 'Demo';
})();IIFE выполняется сразу и создает отдельную scope для локальных переменных. До ES modules ее часто использовали, чтобы не загрязнять global scope и скрывать implementation details. |
Реализуйте chunk(array, size).
|
Что проверяет: работу с массивами, границы цикла, edge cases. Функция должна разбить массив на группы длиной function chunk(items, size) {
if (size <= 0) {
throw new Error('Size must be positive');
}
const result = [];
for (let index = 0; index < items.length; index += size) {
result.push(items.slice(index, index + size));
}
return result;
}
chunk([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]]На интервью важно обсудить пустой массив, |
Реализуйте retry для асинхронной операции.
|
Что проверяет: Promise, async function retry(operation, options) {
const {attempts, delayMs} = options;
let attempt = 0;
while (attempt < attempts) {
try {
return await operation();
} catch (error) {
attempt += 1;
if (attempt >= attempts) {
throw error;
}
await new Promise((resolve) => setTimeout(resolve, delayMs * attempt));
}
}
}В production-версии стоит добавить Follow-up вопросы:
|
Реализуйте простой EventEmitter.
|
Что проверяет: структуры данных, callbacks, cleanup подписок. class EventEmitter {
#listeners = new Map();
on(eventName, listener) {
const listeners = this.#listeners.get(eventName) ?? new Set();
listeners.add(listener);
this.#listeners.set(eventName, listeners);
return () => {
listeners.delete(listener);
};
}
emit(eventName, payload) {
const listeners = this.#listeners.get(eventName);
if (!listeners) {
return;
}
for (const listener of [...listeners]) {
listener(payload);
}
}
}Копия |
Реализуйте безопасное чтение значения по строковому пути.
|
Что проверяет: работу с объектами, optional access, защиту от некорректного пути. function getByPath(source, path, fallback) {
const parts = path.split('.');
let current = source;
for (const part of parts) {
const canReadProperty = current !== null && typeof current === 'object' && Object.hasOwn(current, part);
if (!canReadProperty) {
return fallback;
}
current = current[part];
}
return current;
}
getByPath({user: {name: 'Ada'}}, 'user.name', ''); // 'Ada'В ответе важно не использовать небезопасный |
Как реализовать polyfill для Promise.all?
|
Нужно сохранить порядок результатов, принять обычные значения вместе с promises и отклонить итоговый promise при первой ошибке. function promiseAll(values) {
return new Promise((resolve, reject) => {
const results = [];
let completed = 0;
if (values.length === 0) {
resolve([]);
return;
}
values.forEach((value, index) => {
Promise.resolve(value)
.then((result) => {
results[index] = result;
completed += 1;
if (completed === values.length) {
resolve(results);
}
})
.catch(reject);
});
});
}Частые ошибки: возвращать результаты в порядке завершения, забывать пустой массив, не оборачивать обычные значения через
|
