Na rozmowach z JavaScriptu najczęściej pada kilkanaście pytań: różnica międzyvar,leticonst,==kontra===, closures,this, prototypy, event loop oraz Promise iasync/await. Rekruter nie sprawdza definicji z pamięci, tylko to, czy rozumiesz, dlaczego kod zachowuje się tak, a nie inaczej. Dobra odpowiedź to jedno zdanie definicji i krótki przykład.
Rozmowa techniczna z JavaScriptu rzadko jest egzaminem z całej specyfikacji. Najczęściej to te same kilkanaście tematów, w których rekruter sprawdza, czy rozumiesz mechanikę języka, a nie tylko potrafisz skopiować kod z dokumentacji. Poniżej znajdziesz pytania, które wracają na rozmowach na stanowisko juniora, wraz z tym, jak na nie odpowiadać. Jeśli dopiero układasz plan przygotowań, zacznij od przygotowań do rekrutacji na juniora developera, a ten tekst potraktuj jako listę kontrolną z samego JavaScriptu.
Jak odpowiadać na pytania rekrutacyjne z JavaScript?
Najlepiej w trzech krokach: jedno zdanie definicji, krótki przykład kodu, a na końcu informacja, kiedy to ma znaczenie w praktyce. Taka odpowiedź zajmuje 30–60 sekund i pokazuje, że rozumiesz temat, a nie wkułeś formułkę. Jeśli nie znasz odpowiedzi, powiedz, co wiesz, i zacznij rozumować na głos. Rekruterzy oceniają sposób myślenia, a nie tylko wynik.
Poniższe pytania są podzielone od podstaw do tematów, które częściej pojawiają się przy ofertach na mid-poziom.
Podstawy języka
Czym różnią się var, let i const?
var ma zasięg funkcyjny, a let i const blokowy (ograniczony klamrami {}). const nie pozwala przypisać wartości ponownie, ale nie zamraża obiektu, więc jego zawartość nadal można zmieniać. W praktyce domyślnie używa się const, a let tylko tam, gdzie wartość faktycznie się zmienia.
const user = { name: 'Anna' };
user.name = 'Ola'; // działa, zmieniamy zawartość obiektu
user = {}; // TypeError: nie można ponownie przypisać const
Co to jest hoisting i Temporal Dead Zone?
Hoisting to zachowanie, w którym deklaracje są „podnoszone” na początek swojego zasięgu jeszcze przed wykonaniem kodu. Zmienna var jest wtedy dostępna z wartością undefined, natomiast let i const istnieją, ale do momentu deklaracji znajdują się w Temporal Dead Zone, czyli odczyt kończy się błędem ReferenceError. Deklaracje funkcji (function foo() {}) są podnoszone razem z ciałem, więc można je wywołać przed definicją.
console.log(a); // undefined
var a = 1;
console.log(b); // ReferenceError
let b = 2;
Czym różni się == od ===?
== porównuje wartości po niejawnej konwersji typów, a === porównuje wartość i typ bez konwersji. Dlatego '5' == 5 daje true, a '5' === 5 daje false. W kodzie produkcyjnym używa się niemal zawsze ===, a wyjątkiem bywa x == null, które łapie jednocześnie null i undefined. Więcej o operatorach znajdziesz w artykule o operatorach logicznych w JavaScript.
Jaka jest różnica między null a undefined?
undefined oznacza, że wartość nie została przypisana (np. zmienna bez inicjalizacji albo brakująca właściwość obiektu). null to wartość, którą programista ustawia świadomie jako „brak wartości”. Dodatkowo typeof null zwraca 'object', co jest znanym błędem z początków języka, którego nie naprawiono ze względu na kompatybilność.
Jakie są typy danych w JavaScript?
Typów prostych (prymitywów) jest siedem: string, number, boolean, null, undefined, symbol i bigint. Wszystko inne to obiekt, także tablice i funkcje. Prymitywy są kopiowane przez wartość, a obiekty przez referencję, i właśnie to bywa źródłem pytań dodatkowych, na przykład o to, dlaczego zmiana kopii tablicy zmienia oryginał. Podstawy obiektów opisuje artykuł o obiektach w JavaScript.
Funkcje, zasięg i this
Co to jest closure (domknięcie)?
Closure to funkcja, która „pamięta” zmienne z zasięgu, w którym została utworzona, nawet po zakończeniu działania funkcji zewnętrznej. Najczęstsze zastosowania to prywatny stan, fabryki funkcji i funkcje wywoływane później, np. w obsłudze zdarzeń.
function createCounter() {
let count = 0;
return () => ++count;
}
const counter = createCounter();
counter(); // 1
counter(); // 2
Pytanie uzupełniające brzmi zwykle: dlaczego pętla for z var i setTimeout wypisuje tę samą wartość? Odpowiedź: wszystkie callbacki dzielą jedną zmienną var, a przy let każda iteracja ma własną kopię.
Jak działa this w JavaScript?
Wartość this zależy od sposobu wywołania funkcji, a nie miejsca jej zapisu. W metodzie obiektu wskazuje na ten obiekt, w zwykłym wywołaniu na undefined (tryb ścisły) lub obiekt globalny, a metody call, apply i bind pozwalają ustawić go ręcznie. Funkcje strzałkowe nie mają własnego this i biorą go z otaczającego zasięgu, co dokładnie opisujemy w tekście o funkcjach strzałkowych i this.
Czym różni się funkcja strzałkowa od zwykłej?
Funkcja strzałkowa nie ma własnego this, nie ma obiektu arguments i nie można jej użyć jako konstruktora (new). Ma też krótszą składnię z niejawnym zwracaniem wartości. To dlatego nie nadaje się na metody obiektu, które potrzebują this wskazującego na ten obiekt.
Co to jest funkcja wyższego rzędu i callback?
Funkcja wyższego rzędu przyjmuje inną funkcję jako argument albo ją zwraca. Callback to funkcja przekazana do innej, aby została wywołana później. Przykłady z codziennej pracy to map, filter, addEventListener i setTimeout. Szerzej opisujemy to w artykule o funkcjach callback w JavaScript.
Obiekty i prototypy
Jak działa dziedziczenie prototypowe?
Każdy obiekt ma wewnętrzne powiązanie z innym obiektem, swoim prototypem. Gdy odczytujesz właściwość, której obiekt nie ma, silnik szuka jej w prototypie, potem w prototypie prototypu, aż do null. To tak zwany łańcuch prototypów. Składnia class jest „lukrem składniowym” nad tym mechanizmem, więc pod spodem nadal działają prototypy. Praktyczne ujęcie znajdziesz w tekście o programowaniu obiektowym w JavaScript.
Co robią destrukturyzacja i operatory spread/rest?
Destrukturyzacja wyciąga wartości z tablic i obiektów do zmiennych (const { name } = user). Spread (...) rozwija elementy do nowej tablicy lub obiektu, a rest zbiera pozostałe argumenty lub elementy. Pytanie często jest zadaniem w tle: rekruter prosi o skopiowanie obiektu bez mutacji. Różnice między spread a rest opisuje artykuł o rozproszeniu i parametrze reszty, a samą składnię wyjaśnia tekst o destrukturyzacji w JavaScript.
Czym różni się płytka kopia od głębokiej?
Płytka kopia ({ ...obj }, Object.assign) kopiuje tylko pierwszy poziom, a zagnieżdżone obiekty nadal wskazują na te same referencje. Głęboką kopię zrobisz dziś najprościej przez structuredClone(obj). Starsze rozwiązanie JSON.parse(JSON.stringify(obj)) gubi funkcje, daty i undefined, więc warto je znać, ale i znać jego ograniczenia.
Asynchroniczność
Jak działa event loop?
JavaScript wykonuje kod w jednym wątku. Synchroniczny kod trafia na stos wywołań (call stack), a zadania asynchroniczne, takie jak timery czy odpowiedzi sieciowe, czekają w kolejkach. Event loop pobiera zadanie z kolejki dopiero wtedy, gdy stos jest pusty, przy czym kolejka mikrozadań (Promise) jest opróżniana przed kolejką makrozadań (setTimeout). Dlatego setTimeout(fn, 0) nie uruchomi się „od razu”, tylko po zakończeniu bieżącego kodu i mikrozadań.
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
// 1, 4, 3, 2
To klasyczne zadanie „co wypisze ten kod?” i warto umieć wyjaśnić kolejność krok po kroku.
Czym jest Promise i jak działa async/await?
Promise reprezentuje wartość, która będzie dostępna w przyszłości, i ma trzy stany: oczekujący, spełniony i odrzucony. async/await to wygodniejszy zapis tego samego mechanizmu: await wstrzymuje funkcję async do czasu rozwiązania Promise, nie blokując reszty programu. Błędy obsługujesz przez try/catch albo .catch().
async function loadUser(id) {
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) throw new Error('Błąd HTTP: ' + response.status);
return await response.json();
} catch (error) {
console.error('Nie udało się pobrać użytkownika', error);
}
}
Częsta pułapka: fetch nie odrzuca Promise przy statusie 404 czy 500, dlatego trzeba sprawdzić response.ok. Jak testować taki kod, opisujemy w artykule o testowaniu kodu asynchronicznego w JavaScript.
Czym różni się Promise.all od Promise.allSettled?
Promise.all kończy się błędem, gdy odrzucony zostanie którykolwiek z Promise, a Promise.allSettled zawsze czeka na wszystkie i zwraca status każdego. Pierwszego używasz, gdy potrzebujesz wszystkich wyników naraz, drugiego, gdy pojedyncze porażki są akceptowalne.
Tablice, DOM i zadania praktyczne
Czym różnią się map, filter i reduce?
map zwraca nową tablicę o tej samej długości po przekształceniu każdego elementu, filter zwraca elementy spełniające warunek, a reduce łączy tablicę w jedną wartość. Żadna z tych metod nie zmienia oryginału. Powtórkę wszystkich metod znajdziesz w artykule o metodach tablicowych w JavaScript, a samo sort (które mutuje tablicę) opisuje tekst o metodzie sort.
Co to jest propagacja zdarzeń i event delegation?
Zdarzenie najpierw schodzi w dół drzewa DOM (faza przechwytywania), a potem wraca w górę (faza bąbelkowania). Event delegation polega na tym, że jeden listener na rodzicu obsługuje zdarzenia od wielu dzieci, także tworzonych dynamicznie, i rozpoznaje źródło przez event.target. Dzięki temu nie dodajesz osobnego nasłuchiwania do każdego elementu, co opisuje artykuł o nasłuchiwaniu zdarzeń na elementach tworzonych dynamicznie. Szersze tło to DOM w JavaScript.
Co to jest debounce i throttle?
Debounce opóźnia wywołanie funkcji do momentu, aż użytkownik przestanie wywoływać zdarzenie przez zadany czas (np. wyszukiwarka wpisywana literka po literce). Throttle ogranicza wywołania do jednego na ustalony odstęp (np. obsługa scrolla). To częste zadanie „napisz debounce”, bo sprawdza jednocześnie closures i timery.
function debounce(fn, delay) {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn(...args), delay);
};
}
Jakie zadania z kodem pojawiają się na rozmowach?
Najczęściej: odwrócenie stringa, sprawdzenie palindromu, usunięcie duplikatów z tablicy, policzenie wystąpień elementów, spłaszczenie zagnieżdżonej tablicy, własna implementacja map/debounce oraz proste zadania z rekurencją, np. silnia lub ciąg Fibonacciego. Pamiętaj, że rekruter patrzy na sposób rozwiązywania: nazwij zmienne czytelnie, omów przypadki brzegowe (pusta tablica, null) i powiedz, jaka jest złożoność. Powtórkę rekurencji znajdziesz w artykule o rekurencji jako alternatywie dla pętli.
Jak przygotować się do rozmowy z JavaScriptu?
Samo czytanie odpowiedzi nie wystarczy, bo pytania uzupełniające szybko pokazują, czy rozumiesz temat. Skuteczniej działa trening na własnych projektach i głośne tłumaczenie tematów komuś innemu (albo mentorowi). Dla porządku:
| Obszar | Co umieć wyjaśnić własnymi słowami |
|---|---|
| Podstawy | var/let/const, hoisting, == vs ===, typy |
| Funkcje | closures, this, funkcje strzałkowe, callbacki |
| Obiekty | prototypy, class, kopiowanie, destrukturyzacja |
| Async | event loop, Promise, async/await, obsługa błędów |
| Praktyka | metody tablicowe, DOM, debounce, proste algorytmy |
Na czym juniorzy najczęściej tracą punkty?
Nie na trudnych tematach, tylko na trzech drobiazgach, które wychodzą dopiero przy pytaniach uzupełniających:
typeof null. Zwraca'object'. Kto tego nie zna, często odpowiada, żenulljest obiektem. Poprawna odpowiedź: to historyczny błąd języka, anulljest prymitywem.fetchi statusy HTTP.fetchnie odrzuca Promise przy odpowiedzi 404 czy 500, tylko przy awarii sieci. Brak sprawdzeniaresponse.okto częsty sygnał, że kod nie był testowany pod kątem błędów.varw pętli zsetTimeout. Wszystkie callbacki widzą tę samą zmienną, więc wypisują ostatnią wartość. Nie wystarczy napisać, że trzeba użyćlet. Wyjaśnij, żelettworzy osobną kopię zmiennej w każdej iteracji.
Jak brzmi dobra odpowiedź, a jak słaba?
Weźmy pytanie o closures:
| Odpowiedź | Dlaczego | |
|---|---|---|
| Słaba | „Closure to funkcja w funkcji.” | To opis składni, nie mechanizmu, a pytanie uzupełniające od razu to pokaże. |
| Dobra | „Closure to funkcja, która zachowuje dostęp do zmiennych z zasięgu, w którym powstała, także po jego zakończeniu. Dzięki temu mogę np. zrobić licznik z prywatnym stanem.” | Jest definicja, mechanizm i zastosowanie. |
Ten sam schemat działa przy każdym pytaniu z tego artykułu: definicja, mechanizm, przykład użycia.
Co zrobić, gdy nie znasz odpowiedzi?
Nie zgaduj i nie milcz. Powiedz, co wiesz na pewno, a potem rozumuj na głos, np.: „Nie pamiętam dokładnej nazwy, ale wiem, że to dotyczy kolejności wykonania kodu asynchronicznego, więc sprawdziłbym to w dokumentacji MDN.” Taka odpowiedź pokazuje, że potrafisz sobie poradzić w pracy, a umiejętność znalezienia odpowiedzi jest w codziennej pracy ważniejsza niż pamięć do szczegółów.
Dobrze też wiedzieć, jak wygląda sama rozmowa. W podcaście Radek Wojtysiak opowiada o rekrutacji technicznej: jak się do niej przygotować i co naprawdę jest sprawdzane.
Znajomość odpowiedzi to połowa sukcesu, drugą jest znalezienie rozmów, na które warto się umówić. Jeśli szukasz ofert, sięgnij po darmowy poradnik o tym, jak szukać pracy w 2026 roku, w którym zebraliśmy 3 sposoby poszukiwania pracy, które u naszych podopiecznych dają najlepsze efekty. Całość procesu, od CV po rozmowę, opisuje artykuł o tym, jak przejść cały proces rekrutacyjny na juniora.
Jeśli braki wyjdą przy pytaniach o async albo this, to sygnał, że warto uporządkować wiedzę z mentorem. Program ścieżki Frontend obejmuje JavaScript od podstaw po pracę z API, a ścieżka Zaawansowany JS rozwija tematy, o które pytają przy ofertach na wyższe stanowiska. Warto też poznać nowości z ECMAScript 2026, bo rekruterzy lubią zapytać, co ostatnio przyszło do języka.


