15 Najtrudniejszych Pytań Rekrutacyjnych z JavaScript
Dlaczego ten kod wypisuje trzy razy liczbę 3 zamiast 0, 1, 2? To klasyczne pytanie z pętlą for i setTimeout potrafi zaskakiwać nawet programistów z wieloletnim doświadczeniem. Odpowiedź wymaga połączenia wiedzy o closure, scope i asynchroniczności - a właśnie takie pytania najczęściej padają na rozmowach rekrutacyjnych.
Najtrudniejsze pytania z JavaScript nie dotyczą egzotycznych API czy obscurycznych edge case'ów. Dotyczą fundamentów języka - rzeczy, które używamy codziennie, ale rzadko się nad nimi zastanawiamy.
Poniżej znajdziesz 15 pytań, które konsekwentnie sprawiają trudności nawet doświadczonym programistom. Nie są to pytania-zagadki ani gotcha questions wymyślone żeby kogoś przyłapać. To pytania, które naprawdę weryfikują zrozumienie JavaScript na głębokim poziomie.
1. Closure - Fundament, Który Wszyscy Używają, Ale Niewielu Rozumie
Zacznijmy od closure, bo to temat, który pojawia się na praktycznie każdej rozmowie rekrutacyjnej. Co ciekawe, większość programistów używa closure codziennie - w React hooks, w event handlerach, w callbackach - ale gdy przychodzi do wyjaśnienia mechanizmu, zaczynają się problemy.
Odpowiedź w 30 sekund
Gdy rekruter pyta "Co to jest closure?", moja odpowiedź brzmi tak:
Closure to funkcja, która 'pamięta' zmienne z zakresu, w którym została utworzona, nawet gdy ten zakres już nie istnieje. Funkcja wewnętrzna ma dostęp do zmiennych funkcji zewnętrznej nawet po jej zakończeniu. Używamy tego do tworzenia prywatnych zmiennych i zachowania stanu między wywołaniami.
Poczekaj na reakcję rekrutera. Jeśli chce więcej szczegółów, kontynuuj.
Odpowiedź w 2 minuty
Technicznie, closure to połączenie funkcji i jej środowiska leksykalnego - czyli miejsca w kodzie, gdzie została zdefiniowana. W JavaScript każda funkcja tworzy closure. Kluczowe jest to, że zmienne nie są kopiowane do closure - zachowywana jest referencja do nich.
Pokażę na przykładzie:
function createCounter() {
let count = 0; // ta zmienna jest 'zamknięta' w closure
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
// Każde wywołanie createCounter tworzy nowy, niezależny closure
const anotherCounter = createCounter();
console.log(anotherCounter()); // 1 (nie 4!)
Zwróć uwagę, że zmienna count nie jest dostępna z zewnątrz - nie możemy napisać counter.count ani createCounter.count. To właśnie dlatego closure jest podstawą wzorca modułu i emulacji prywatnych zmiennych w JavaScript.
Klasyczny Problem: Pętla i setTimeout
Niemal na pewno spotkasz się z tym pytaniem lub jego wariacją. Rekruter pokazuje kod:
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
I pyta: "Co zostanie wypisane i dlaczego?"
Wielu kandydatów odpowiada "0, 1, 2". Błąd. Wypisane zostanie "3, 3, 3".
Dlaczego? Bo var ma zakres funkcyjny, nie blokowy. Istnieje tylko jedna zmienna i dla całej pętli. Gdy callbacki setTimeout się wykonują (po sekundzie), pętla już dawno się zakończyła, a i ma wartość 3. Wszystkie trzy funkcje closure odwołują się do tej samej zmiennej.
Rozwiązanie z let:
for (let i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
// Wypisuje: 0, 1, 2
Z let każda iteracja pętli tworzy nowy binding dla i. Każdy callback ma swoje własne i.
Rozwiązanie z IIFE (przed ES6):
for (var i = 0; i < 3; i++) {
(function(j) {
setTimeout(function() {
console.log(j);
}, 1000);
})(i);
}
// Wypisuje: 0, 1, 2
IIFE tworzy nowy zakres dla każdej iteracji, "przechwytując" aktualną wartość i w parametrze j.
2. Hoisting - Dlaczego JavaScript Wydaje Się Czytać Kod Od Końca
Hoisting to mechanizm, który sprawia, że możesz użyć zmiennej lub funkcji przed jej deklaracją w kodzie. Brzmi magicznie, ale gdy zrozumiesz jak działa, przestanie być zagadką.
Odpowiedź w 30 sekund
Hoisting to przenoszenie deklaracji zmiennych i funkcji na początek ich zakresu podczas fazy kompilacji. Ale uwaga - przenoszone są tylko deklaracje, nie inicjalizacje. Dla
varoznacza to, że zmienna istnieje od początku funkcji z wartościąundefined. Dlaleticonstdeklaracja jest podniesiona, ale zmienna jest w 'temporal dead zone' do momentu definicji.
Odpowiedź w 2 minuty
Pokażę różnicę na kodzie:
console.log(a); // undefined (nie błąd!)
var a = 5;
console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 10;
Dla interpretera pierwszy fragment wygląda tak:
var a; // deklaracja przeniesiona na górę
console.log(a); // undefined
a = 5; // inicjalizacja zostaje na miejscu
Funkcje deklarowane słowem function są hoistowane w całości - razem z ciałem:
greet(); // "Hello!" - działa!
function greet() {
console.log("Hello!");
}
Ale wyrażenia funkcyjne zachowują się jak zmienne:
greet(); // TypeError: greet is not a function
var greet = function() {
console.log("Hello!");
};
Na Co Zwracają Uwagę Rekruterzy
Kandydat, który rozumie hoisting, nie napisze kodu polegającego na tym mechanizmie. Deklaracje powinny być na początku zakresu - nie dlatego, że muszą, ale dlatego, że to czytelniejsze. Dobry programista zna hoisting, żeby debugować cudzy kod i rozumieć dziwne zachowania, ale w swoim kodzie unika niejednoznaczności.
3. Słowo Kluczowe this - Najbardziej Niespójny Element JavaScript
Jeśli miałbym wskazać jeden temat, który powoduje najwięcej zamieszania w JavaScript, byłoby to this. W innych językach this zawsze wskazuje na obiekt, w którym jesteśmy. W JavaScript wartość this zależy od sposobu wywołania funkcji.
Odpowiedź w 30 sekund
thisw JavaScript nie jest ustalane przy definicji funkcji, tylko przy jej wywołaniu. W metodzie obiektuthisto obiekt. W zwykłej funkcji towindowlubundefinedw strict mode. W funkcji strzałkowejthisjest dziedziczone z zakresu zewnętrznego. Można też ustawićthisjawnie przezcall,applylubbind.
Pięć Kontekstów this
Pozwól, że pokażę wszystkie przypadki:
// 1. Metoda obiektu - this to obiekt
const user = {
name: 'Anna',
greet() {
console.log(`Hello, ${this.name}`);
}
};
user.greet(); // "Hello, Anna"
// 2. Zwykła funkcja - this to window (lub undefined w strict mode)
function showThis() {
console.log(this);
}
showThis(); // Window {...} lub undefined
// 3. Funkcja strzałkowa - this z zakresu zewnętrznego
const user2 = {
name: 'Bartek',
greet: () => {
console.log(this.name); // this to Window, nie user2!
},
greetDelayed() {
setTimeout(() => {
console.log(this.name); // this to user2 - dziedziczone!
}, 100);
}
};
// 4. Konstruktor (new) - this to nowy obiekt
function Person(name) {
this.name = name;
}
const person = new Person('Celina');
console.log(person.name); // "Celina"
// 5. call/apply/bind - this ustawione jawnie
function introduce() {
console.log(`I'm ${this.name}`);
}
const dev = { name: 'Developer' };
introduce.call(dev); // "I'm Developer"
Klasyczny Problem: Zagubiony Kontekst
Rekruter pokazuje kod:
const user = {
name: 'Anna',
greet() {
console.log(`Hello, ${this.name}`);
}
};
const greetFn = user.greet;
greetFn(); // Co zostanie wypisane?
Odpowiedź: "Hello, undefined" (lub błąd w strict mode).
Gdy przypisujemy metodę do zmiennej, tracimy kontekst obiektu. greetFn to teraz zwykła funkcja, wywołana bez kontekstu. Rozwiązania:
// Użyj bind
const greetFn = user.greet.bind(user);
greetFn(); // "Hello, Anna"
// Lub wywołaj z kontekstem
const greetFn = user.greet;
greetFn.call(user); // "Hello, Anna"
// Lub użyj funkcji strzałkowej w definicji
const user = {
name: 'Anna',
greet: () => { // Uwaga: teraz this to Window!
console.log(`Hello, ${this.name}`);
}
};
Ten ostatni przykład to pułapka - funkcja strzałkowa jako metoda obiektu to prawie zawsze błąd, bo this będzie z zakresu zewnętrznego (globalnego), nie z obiektu.
4. Różnica Między == i ===
To pytanie wydaje się proste, ale rekruterzy lubią je rozwijać o edge case'y i praktyczne zastosowania.
Odpowiedź w 30 sekund
==porównuje wartości po konwersji typów - nazywamy to loose equality.===porównuje wartości i typy bez konwersji - strict equality. Zawsze używaj===, chyba że świadomie chcesz konwersji. Jedyny wyjątek:x == nulldo sprawdzenia zarówno null jak i undefined.
Tabela Niespodzianek
Oto przypadki, które zaskakują nawet doświadczonych programistów:
| Wyrażenie | == |
=== |
Dlaczego? |
|---|---|---|---|
1 == '1' |
true |
false |
String konwertowany do Number |
0 == false |
true |
false |
Boolean konwertowany do Number |
null == undefined |
true |
false |
Specjalna reguła w specyfikacji |
NaN == NaN |
false |
false |
NaN nie jest równe niczemu! |
[] == false |
true |
false |
[] -> '' -> 0, false -> 0 |
[] == ![] |
true |
false |
![] to false, [] to 0 |
Ten ostatni przypadek, [] == ![] zwracające true, to ulubione pytanie rekruterów. Wyjaśnienie: ![] to false (bo tablica jest truthy). Następnie [] == false konwertuje obie strony do liczb: [] staje się pustym stringiem, który staje się 0, a false też staje się 0. 0 == 0 to true.
5. Promise i Async/Await - Serce Nowoczesnego JavaScript
Asynchroniczność to serce JavaScript w przeglądarce i Node.js. Rekruterzy sprawdzają nie tylko czy znasz składnię, ale czy rozumiesz jak to działa pod spodem.
Odpowiedź w 30 sekund
Promise to obiekt reprezentujący przyszły wynik operacji asynchronicznej - może być pending, fulfilled lub rejected. Async/await to składnia zbudowana na Promise, pozwalająca pisać kod asynchroniczny jak synchroniczny. Funkcja
asynczawsze zwraca Promise.awaitwstrzymuje wykonanie do rozwiązania Promise, ale nie blokuje głównego wątku.
Odpowiedź w 2 minuty
// Promise - tradycyjna składnia
function fetchUser(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id > 0) {
resolve({ id, name: 'User ' + id });
} else {
reject(new Error('Invalid ID'));
}
}, 1000);
});
}
// Użycie z .then/.catch
fetchUser(1)
.then(user => console.log(user))
.catch(error => console.error(error));
// To samo z async/await
async function getUser(id) {
try {
const user = await fetchUser(id);
console.log(user);
} catch (error) {
console.error(error);
}
}
Klasyczny Problem: Kolejność Wykonania
Rekruter pokazuje kod i pyta o kolejność wypisania:
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
Odpowiedź: 1, 4, 3, 2
Dlaczego nie 1, 4, 2, 3? Bo Promise callbacks (microtasks) mają wyższy priorytet niż setTimeout callbacks (macrotasks). Event loop najpierw opróżnia microtask queue przed wzięciem kolejnego task z task queue.
Równoległe vs Sekwencyjne Wywołania
To częsty błąd - sekwencyjne await tam, gdzie można równolegle:
// ZŁE - sekwencyjne, wolne
async function slow() {
const user = await fetchUser(1); // czeka 1s
const posts = await fetchPosts(1); // czeka kolejną 1s
return { user, posts }; // łącznie 2s
}
// DOBRE - równoległe, szybkie
async function fast() {
const [user, posts] = await Promise.all([
fetchUser(1),
fetchPosts(1)
]);
return { user, posts }; // łącznie 1s
}
6. Event Loop - Jak JavaScript Robi Wiele Rzeczy Naraz
JavaScript jest jednowątkowy, ale obsługuje tysiące równoczesnych operacji. Jak to możliwe? Dzięki Event Loop.
Odpowiedź w 30 sekund
Event Loop to mechanizm zarządzający wykonaniem kodu w JavaScript. Przetwarza synchroniczny kod z call stack, następnie microtasks (Promise callbacks), a potem macrotasks (setTimeout, I/O). Jest jednowątkowy, ale dzięki asynchroniczności nie blokuje - operacje I/O są delegowane do systemu, a wyniki wracają przez callbacki.
Wizualizacja Cyklu
Wyobraź sobie Event Loop jako pętlę nieskończoną:
while (true) {
1. Wykonaj wszystko z Call Stack
2. Wykonaj WSZYSTKIE microtasks (Promise.then, queueMicrotask)
3. Opcjonalnie: Renderuj UI (co ~16ms)
4. Weź JEDEN task z Task Queue (setTimeout, setInterval, I/O)
5. Wróć do kroku 1
}
Kluczowa różnica: wszystkie microtasks vs jeden macrotask. Dlatego ten kod może zablokować przeglądarkę:
function blockForever() {
Promise.resolve().then(blockForever);
}
blockForever(); // Nieskończona pętla microtasks!
Przeglądarka nigdy nie dojdzie do renderowania ani obsługi kliknięć.
7. Prototypy i Dziedziczenie
ES6 dało nam klasy, ale pod spodem nadal działają prototypy. Rekruterzy sprawdzają, czy rozumiesz ten mechanizm.
Odpowiedź w 30 sekund
W JavaScript obiekty dziedziczą przez łańcuch prototypów. Każdy obiekt ma ukryty link
[[Prototype]]do innego obiektu. Gdy szukamy właściwości, JavaScript sprawdza obiekt, potem jego prototyp, potem prototyp prototypu, aż donull. Klasy ES6 to składniowy cukier na tę mechanikę.
Jak To Działa
// Każda funkcja ma właściwość prototype
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(`${this.name} makes a sound`);
};
const dog = new Animal('Rex');
dog.speak(); // "Rex makes a sound"
// dog nie ma metody speak - jest ona w Animal.prototype
console.log(dog.hasOwnProperty('speak')); // false
console.log(dog.hasOwnProperty('name')); // true
// Łańcuch: dog -> Animal.prototype -> Object.prototype -> null
Klasy ES6 vs Prototypy
// To samo z klasą ES6
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a sound`);
}
}
// Pod spodem nadal prototypy!
console.log(typeof Animal); // "function"
console.log(Animal.prototype.speak); // [Function: speak]
8. Typy Wartościowe vs Referencyjne
To fundamentalne rozróżnienie, które wpływa na każdy aspekt pracy z JavaScript.
Odpowiedź w 30 sekund
Typy prymitywne (number, string, boolean, null, undefined, symbol, bigint) są kopiowane przez wartość. Obiekty, tablice i funkcje są przekazywane przez referencję - kopiowana jest tylko referencja do tego samego miejsca w pamięci. Dlatego modyfikacja obiektu w funkcji zmienia oryginalny obiekt.
Kod Demonstrujący Różnicę
// Prymitywy - kopiowanie wartości
let a = 5;
let b = a;
b = 10;
console.log(a); // 5 - niezmienione
// Obiekty - kopiowanie referencji
let obj1 = { value: 5 };
let obj2 = obj1;
obj2.value = 10;
console.log(obj1.value); // 10 - zmienione!
// Jak skopiować obiekt?
let obj3 = { ...obj1 }; // Shallow copy
let obj4 = JSON.parse(JSON.stringify(obj1)); // Deep copy (uwaga: gubi funkcje i daty)
let obj5 = structuredClone(obj1); // Deep copy (nowoczesne przeglądarki)
Klasyczny Problem: Funkcje i Obiekty
function modifyPrimitive(x) {
x = x + 1;
}
function modifyObject(obj) {
obj.value = obj.value + 1;
}
let num = 5;
modifyPrimitive(num);
console.log(num); // 5 - niezmienione
let obj = { value: 5 };
modifyObject(obj);
console.log(obj.value); // 6 - zmienione!
9. Spread i Rest Operator
Te trzy kropki ... robią różne rzeczy w zależności od kontekstu. Rekruterzy lubią sprawdzać, czy kandydat rozumie obie strony.
Odpowiedź w 30 sekund
Spread
...'rozpyla' elementy tablicy lub właściwości obiektu - używamy go do kopiowania, łączenia, przekazywania argumentów. Rest...'zbiera' wiele elementów w jeden - używamy go w parametrach funkcji i destrukturyzacji. Ta sama składnia, przeciwne działanie.
Spread w Akcji
// Kopiowanie tablicy
const arr = [1, 2, 3];
const copy = [...arr];
// Łączenie tablic
const merged = [...arr, 4, 5, ...arr]; // [1,2,3,4,5,1,2,3]
// Kopiowanie obiektu
const user = { name: 'Anna', age: 25 };
const userCopy = { ...user, age: 26 }; // nadpisuje age
// Argumenty funkcji
const numbers = [1, 2, 3];
console.log(Math.max(...numbers)); // 3
Rest w Akcji
// W parametrach funkcji
function sum(...numbers) {
return numbers.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3, 4); // 10
// W destrukturyzacji tablicy
const [first, second, ...rest] = [1, 2, 3, 4, 5];
// first = 1, second = 2, rest = [3, 4, 5]
// W destrukturyzacji obiektu
const { name, ...others } = { name: 'Anna', age: 25, city: 'Warsaw' };
// name = 'Anna', others = { age: 25, city: 'Warsaw' }
10. var vs let vs const
Proste pytanie, ale rekruterzy lubią wchodzić w szczegóły temporal dead zone i re-deklaracji.
Odpowiedź w 30 sekund
varma zakres funkcyjny, jest hoistowana z undefined, pozwala na re-deklarację.leticonstmają zakres blokowy, są w temporal dead zone do definicji, nie pozwalają na re-deklarację.constdodatkowo nie pozwala na re-przypisanie, ale nie czyni obiektu niemutowalnym.
Kluczowe Różnice
// var - function scope
function example() {
if (true) {
var x = 1;
}
console.log(x); // 1 - dostępne!
}
// let - block scope
function example2() {
if (true) {
let y = 1;
}
console.log(y); // ReferenceError
}
// const - nie można re-przypisać
const obj = { a: 1 };
obj.a = 2; // OK - modyfikacja właściwości
obj = { b: 2 }; // TypeError - re-przypisanie
// var pozwala na re-deklarację
var a = 1;
var a = 2; // OK
// let nie pozwala
let b = 1;
let b = 2; // SyntaxError
11. Funkcje Strzałkowe vs Zwykłe Funkcje
Funkcje strzałkowe to nie tylko krótsza składnia. Mają fundamentalne różnice w zachowaniu.
Odpowiedź w 30 sekund
Funkcje strzałkowe nie mają własnego
this,arguments,superaninew.target- dziedziczą z zakresu zewnętrznego. Nie można ich używać jako konstruktorów. Nie mają właściwościprototype. Są idealne do callbacków i krótkich wyrażeń, ale nie jako metody obiektów.
Kiedy Używać Której
// Strzałkowa w callbacku - idealna
const numbers = [1, 2, 3];
const doubled = numbers.map(n => n * 2);
// Strzałkowa w setTimeout - idealna (dziedziczy this)
const obj = {
name: 'Anna',
greetLater() {
setTimeout(() => {
console.log(this.name); // 'Anna' - this z greetLater
}, 100);
}
};
// Strzałkowa jako metoda - ZŁE
const user = {
name: 'Anna',
greet: () => console.log(this.name) // undefined!
};
// Zwykła funkcja jako konstruktor - OK
function Person(name) {
this.name = name;
}
new Person('Anna');
// Strzałkowa jako konstruktor - BŁĄD
const Person2 = (name) => { this.name = name; };
new Person2('Anna'); // TypeError
12. Object.freeze() vs Object.seal() vs const
Trzy sposoby na "zamrożenie" rzeczy w JavaScript, każdy robi co innego.
Odpowiedź w 30 sekund
constzapobiega re-przypisaniu zmiennej, ale nie chroni zawartości obiektu.Object.seal()blokuje dodawanie i usuwanie właściwości, ale pozwala na modyfikację istniejących.Object.freeze()całkowicie blokuje obiekt - żadnych zmian. Wszystkie są płytkie - zagnieżdżone obiekty nie są chronione.
Porównanie w Kodzie
// const - tylko binding zmiennej
const obj1 = { a: 1 };
obj1.a = 2; // OK
obj1.b = 3; // OK
obj1 = {}; // TypeError
// Object.seal - struktura zamknięta, wartości modyfikowalne
const obj2 = { a: 1 };
Object.seal(obj2);
obj2.a = 2; // OK
obj2.b = 3; // Ignorowane (lub TypeError w strict mode)
delete obj2.a; // Ignorowane
// Object.freeze - całkowicie zamrożony
const obj3 = { a: 1 };
Object.freeze(obj3);
obj3.a = 2; // Ignorowane
obj3.b = 3; // Ignorowane
// Ale uwaga - płytkie!
const obj4 = { nested: { a: 1 } };
Object.freeze(obj4);
obj4.nested.a = 2; // OK - zagnieżdżony obiekt nie jest zamrożony!
13. Destructuring - Więcej Niż Składniowy Cukier
Destrukturyzacja wydaje się prosta, ale ma wiele niuansów, które rekruterzy lubią sprawdzać.
Odpowiedź w 30 sekund
Destrukturyzacja pozwala wyciągać wartości z tablic i obiektów do zmiennych. Obsługuje wartości domyślne, zmianę nazw, zagnieżdżenie i rest operator. W funkcjach pozwala na eleganckie definiowanie nazwanych parametrów z domyślnymi wartościami.
Zaawansowane Przypadki
// Wartości domyślne
const { name = 'Anonymous', age = 0 } = { name: 'Anna' };
// name = 'Anna', age = 0
// Zmiana nazwy
const { name: userName } = { name: 'Anna' };
// userName = 'Anna'
// Zagnieżdżenie
const { address: { city } } = { address: { city: 'Warsaw' } };
// city = 'Warsaw'
// W parametrach funkcji
function createUser({ name, age = 18, role = 'user' } = {}) {
return { name, age, role };
}
createUser({ name: 'Anna' }); // { name: 'Anna', age: 18, role: 'user' }
createUser(); // { name: undefined, age: 18, role: 'user' }
// Zamiana wartości bez zmiennej tymczasowej
let a = 1, b = 2;
[a, b] = [b, a];
// a = 2, b = 1
14. Null vs Undefined - Subtelna Różnica
Oba oznaczają "brak wartości", ale mają różne znaczenia semantyczne.
Odpowiedź w 30 sekund
undefinedoznacza, że zmienna została zadeklarowana, ale nie zainicjalizowana - lub właściwość nie istnieje.nullto jawne przypisanie 'brak wartości' przez programistę. Używajnullgdy chcesz wyrazić intencję,undefinedzostawiaj systemowi. Porównanie:null == undefinedjesttrue, alenull === undefinedjestfalse.
Kiedy Który
// undefined - brak inicjalizacji
let x;
console.log(x); // undefined
// undefined - brak właściwości
const obj = {};
console.log(obj.name); // undefined
// undefined - brak argumentu
function greet(name) {
console.log(name);
}
greet(); // undefined
// null - jawnie brak wartości
const user = {
name: 'Anna',
manager: null // jawnie: nie ma managera
};
// Sprawdzanie obu naraz
if (value == null) {
// value jest null LUB undefined
}
// typeof quirk
console.log(typeof undefined); // "undefined"
console.log(typeof null); // "object" - historyczny błąd JS!
15. Metody Tablicowe - map, filter, reduce, find, some, every
Rekruterzy często proszą o implementację jednej z tych metod lub rozwiązanie problemu z ich użyciem.
Odpowiedź w 30 sekund
maptransformuje każdy element,filterwybiera elementy spełniające warunek,reduceakumuluje tablicę do jednej wartości.findzwraca pierwszy pasujący element,somesprawdza czy którykolwiek pasuje,everysprawdza czy wszystkie pasują. Żadna z nich nie modyfikuje oryginalnej tablicy.
Praktyczne Przykłady
const users = [
{ name: 'Anna', age: 25, active: true },
{ name: 'Bartek', age: 30, active: false },
{ name: 'Celina', age: 28, active: true }
];
// map - transformacja
const names = users.map(u => u.name);
// ['Anna', 'Bartek', 'Celina']
// filter - selekcja
const activeUsers = users.filter(u => u.active);
// [{ name: 'Anna', ... }, { name: 'Celina', ... }]
// reduce - akumulacja
const totalAge = users.reduce((sum, u) => sum + u.age, 0);
// 83
// find - pierwszy pasujący
const bartek = users.find(u => u.name === 'Bartek');
// { name: 'Bartek', age: 30, active: false }
// some - czy którykolwiek
const hasInactive = users.some(u => !u.active);
// true
// every - czy wszystkie
const allActive = users.every(u => u.active);
// false
Klasyczny Problem: Implementacja reduce
Rekruter może poprosić o implementację reduce:
function myReduce(array, callback, initialValue) {
let accumulator = initialValue !== undefined ? initialValue : array[0];
let startIndex = initialValue !== undefined ? 0 : 1;
for (let i = startIndex; i < array.length; i++) {
accumulator = callback(accumulator, array[i], i, array);
}
return accumulator;
}
// Test
const sum = myReduce([1, 2, 3, 4], (acc, val) => acc + val, 0);
console.log(sum); // 10
Na Co Rekruterzy Naprawdę Zwracają Uwagę
Po przeprowadzeniu setek rozmów rekrutacyjnych mogę powiedzieć, że nie chodzi tylko o znajomość odpowiedzi. Sprawdzam:
Czy kandydat potrafi wyjaśnić "dlaczego", nie tylko "co". Każdy może zapamiętać, że closure "zamyka" zmienne. Dobry programista wyjaśni mechanizm środowiska leksykalnego i praktyczne zastosowania.
Czy kandydat przyznaje się, gdy czegoś nie wie. Nikt nie zna wszystkiego. Odpowiedź "Nie jestem pewien, ale podejrzewam że..." jest lepsza niż zgadywanie.
Czy kandydat myśli o edge case'ach. Gdy pytam o ===, czy wspomni o NaN !== NaN? Gdy pytam o const, czy wspomni, że nie czyni obiektu niemutowalnym?
Czy kandydat pisze czytelny kod. Nawet na rozmowie - czy używa sensownych nazw zmiennych, czy formatuje kod.
Praktyka na Koniec
Zanim pójdziesz na rozmowę, upewnij się, że potrafisz odpowiedzieć na te pytania bez googlowania:
- Napisz funkcję
debounce(fn, delay)używając closure. - Wyjaśnij, dlaczego
[] == ![]zwracatrue. - Napisz funkcję, która głęboko klonuje obiekt (bez JSON.parse/stringify).
- Jaka jest różnica między
for...inafor...of? - Jak działa garbage collection w JavaScript?
- Czym różni się
Object.create(null)od{}?
Jeśli potrafisz na nie odpowiedzieć bez wahania, jesteś gotowy na większość rozmów rekrutacyjnych z JavaScript.
Zobacz też
- Pytania Rekrutacyjne z TypeScript dla Początkujących - TypeScript rozszerza JavaScript o statyczne typowanie
- Wzorce Projektowe w JavaScript - Pytania dla Seniorów - zaawansowane wzorce dla doświadczonych programistów
- Najtrudniejsze Pytania z Frameworków JavaScript - React, Angular, Vue i inne frameworki JS
Chcesz Więcej Pytań z JavaScript?
Ten artykuł to tylko wierzchołek góry lodowej. Mamy ponad 150 pytań z JavaScript z szczegółowymi odpowiedziami, przykładami kodu i wyjaśnieniami - od podstaw po zaawansowane tematy jak performance optimization, memory leaks i design patterns.
Sprawdź Pełny Zestaw Pytań JavaScript →
Lub wypróbuj nasz darmowy podgląd pytań JavaScript, żeby zobaczyć więcej pytań w tym stylu.
Napisane przez zespół Flipcards, na podstawie ponad 15 lat doświadczenia w branży IT i setek przeprowadzonych rozmów rekrutacyjnych w firmach takich jak BNY Mellon, UBS i wiodących firmach fintech.
Chcesz więcej pytań rekrutacyjnych?
To tylko jeden temat z naszego kompletnego przewodnika po rozmowach rekrutacyjnych. Uzyskaj dostęp do 800+ pytań z 13 technologii.
