# Питання з JavaScript на співбесіді: розбір із прикладами

> Ключові теми JavaScript-співбесіди з робочим кодом: типи й coercion, scope і замикання, event loop, асинхронність, Map/Set, обробка помилок і live coding. Чекліст за рівнями замість зубріння списків.

- Автор: Юра Скиба (https://cookiesoftware.io)
- Опубліковано: 2026-09-24
- Категорія: JavaScript і TypeScript
- Canonical: https://cookiesoftware.io/blog/javascript-interview-guide

---

**Коротка відповідь.** Списки «топ-100 питань» не працюють, бо інтерв'юер оцінює не завчену відповідь, а розуміння. Ефективніше розібрати сім тем, з яких складається практично будь-яка JavaScript-співбесіда: типи й coercion, scope і замикання, event loop, асинхронність, структури даних, обробка помилок і live coding. Нижче – кожна тема з типовими питаннями, кодом і поясненням, чого насправді чекає інтерв'юер.

## Типи й coercion: звідки беруться «дивацтва» JS

Базове питання: «які типи є в JavaScript?» Відповідь: сім примітивів (string, number, bigint, boolean, undefined, symbol, null) плюс object. Але цікаве починається далі – з неявного приведення типів:

```js
console.log(1 + '2');    // '12'  – число приводиться до рядка
console.log('5' - 2);    // 3     – рядок приводиться до числа
console.log([] + []);    // ''    – обидва масиви → порожні рядки
console.log(0.1 + 0.2 === 0.3); // false – двійкова арифметика з плаваючою комою
```

Чого чекає інтерв'юер: не завчених прикладів, а правила. Оператор `+` за наявності рядка конкатенує, решта арифметичних операторів приводять до числа. Порівняння `==` виконує приведення типів, `===` – ні, тому в реальному коді за замовчуванням використовують строге порівняння. Деталі алгоритмів приведення – у [довіднику MDN з JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript).

Питання-пастка: «чому `typeof null === 'object'`?» Це історична помилка першої реалізації мови, яку не виправили заради сумісності. Знати такі речі корисно, але важливіше показати, що ти розумієш наслідок: перевірка на null робиться через `value === null`, а не через typeof.

## Scope і замикання

Замикання – функція разом із лексичним оточенням, у якому її створили. Класична перевірка:

```js
function createCounter() {
  let count = 0;
  return {
    increment: () => ++count,
    current: () => count,
  };
}

const counter = createCounter();
counter.increment();
counter.increment();
console.log(counter.current()); // 2
```

Питання «як зробити приватну змінну без класів» – саме про це: `count` недоступний ззовні, але живе між викликами. Якщо тема дається важко, у нас є окремий детальний розбір із питаннями-пастками: [closures, scope і hoisting](/blog/closures-scope-hoisting).

## Event loop: чому Promise раніше за setTimeout

Мінімум, який треба вміти пояснити словами: синхронний код виконується в call stack; колбеки таймерів потрапляють у чергу макрозадач; `.then`/`await` – у чергу мікрозадач; мікрозадачі виконуються всі до останньої перед наступною макрозадачею.

```js
console.log('a');
setTimeout(() => console.log('b'), 0);
Promise.resolve().then(() => console.log('c'));
console.log('d');
// a, d, c, b
```

Це питання ставлять майже завжди, і на ньому ж найчастіше «сипляться». Покроковий розбір механіки з трьома класичними задачами – в окремій статті про [event loop у JavaScript](/blog/event-loop-javascript).

## Асинхронність: async/await і помилки

Типове завдання: «перепиши промісовий код на async/await і додай обробку помилок».

```ts
async function loadUser(id: string): Promise<User | null> {
  try {
    const response = await fetch(`/api/users/${id}`);
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }
    return (await response.json()) as User;
  } catch (error) {
    console.error('loadUser failed:', error);
    return null;
  }
}
```

На що дивиться інтерв'юер: чи перевіряєш ти `response.ok` (fetch не reject-ить на 404), чи не ковтаєш помилку мовчки, чи розумієш різницю між послідовним `await` у циклі та паралельним `Promise.all`. Останнє – улюблене уточнення: «а якщо треба завантажити 10 користувачів одночасно?»

## Масиви, Map і Set: обери структуру свідомо

Питання рівня Middle: «чим Map кращий за об'єкт і коли достатньо масиву?» Показова задача – унікальні елементи масиву. Два розв'язки:

```ts
// Варіант 1: через Set – O(n)
function uniqueWithSet(items: number[]): number[] {
  return [...new Set(items)];
}

// Варіант 2: без Set – O(n²), бо includes лінійний
function uniqueNaive(items: number[]): number[] {
  const result: number[] = [];
  for (const item of items) {
    if (!result.includes(item)) result.push(item);
  }
  return result;
}
```

Тест-кейси, які варто назвати вголос: порожній масив, масив без дублікатів, усі елементи однакові, `NaN` (Set вважає `NaN` рівним собі, `includes` – теж, а от `indexOf` – ні). Пояснення складності важить більше за сам код: перший варіант проходить по масиву один раз, другий на кожен елемент шукає лінійно, тому на 10 000 елементів різниця вже помітна. Глибше про складність операцій – у статті про [структури даних JavaScript](/blog/javascript-struktury-danykh).

## this і стрілочні функції

Друга за частотою тема після замикань. Правило коротке: у звичайній функції `this` визначається **способом виклику**; стрілочна функція свого `this` не має і бере його з місця, де була створена.

```js
const user = {
  name: 'Оля',
  hiRegular() {
    console.log(`Привіт, ${this.name}`);
  },
  hiArrow: () => {
    console.log(`Привіт, ${this?.name}`);
  },
};

user.hiRegular();            // 'Привіт, Оля' – виклик через крапку
user.hiArrow();              // 'Привіт, undefined' – стрілка взяла зовнішній this

const detached = user.hiRegular;
detached();                  // 'Привіт, undefined' – втратили контекст
```

Питання «як полагодити `detached`» має три відповіді, і сильний кандидат називає всі: `bind`, обгортка-стрілка, або виклик через об'єкт. А питання «чому стрілка зручна в колбеках класових компонентів і таймерах» перевіряє, чи розумієш ти, що вона *не втрачає* зовнішній `this`, бо просто його не має.

## Обробка помилок

Коротка рамка для відповіді: синхронні помилки ловить `try/catch`; помилки в промісах – `.catch` або `try/catch` навколо `await`; викидай екземпляри `Error` (а не рядки), бо вони мають stack trace; на межах системи (API-запити, парсинг JSON, робота зі сховищем) помилки очікувані, тому обробляй їх явно, а не глобальним «catch усього».

```ts
class ApiError extends Error {
  constructor(
    message: string,
    public readonly status: number
  ) {
    super(message);
    this.name = 'ApiError';
  }
}
```

Питання «навіщо власний клас помилки» – перевірка на досвід: тип помилки дозволяє в одному місці відрізнити «немає мережі» від «токен протух» і відреагувати по-різному.

## Live coding: код пишеться інакше, ніж розповідається

Теоретичні відповіді не рятують, коли перед тобою порожній редактор і таймер. Робочий процес на будь-якій задачі: проговори вимоги → напиши найпростішу робочу версію → назви edge cases → покращуй. Добірка типових задач із повними розборами (debounce, groupBy, flatten, EventEmitter) – у статті [JavaScript live coding: задачі з розбором](/blog/javascript-live-coding-zadachi).

## Як пояснювати складність алгоритму

Не треба бути олімпіадником – достатньо трьох чесних речень: скільки разів проходимо по даних, чи є вкладені цикли по тих самих даних, скільки додаткової пам'яті використовуємо. «Тут O(n), бо один прохід і Set з доступом за константу; пам'ять теж O(n) під Set» – цього формулювання вистачає для більшості frontend-співбесід.

## Чекліст самоперевірки за рівнями

- **Junior:** пояснити типи і coercion своїми словами; написати функцію із замиканням; знати різницю `==`/`===`, `let`/`const`/`var`; розв'язати задачу на масиви без підказок.
- **Middle:** передбачити порядок виводу з setTimeout/Promise; пояснити, коли Map/Set замість об'єкта/масиву і чому; впевнено писати async/await з обробкою помилок; оцінити складність свого рішення.
- **Senior:** усе вище + аргументувати компроміси: чому тут достатньо простого рішення, де межа оптимізації, як це рішення тестувати й підтримувати.

Кожен пункт перевіряється однаково: не «читав і зрозумів», а «написав код і пояснив уголос». Якщо готуєшся системно, вбудуй ці теми в тижневий план зі статті [як підготуватися до технічної співбесіди за 30 днів](/blog/pidhotovka-do-tekhnichnoi-spivbesidy).
