Hoisting

Mechanizm JavaScriptu polegający na przeniesieniu deklaracji zmiennych i funkcji na początek zakresu przed wykonaniem kodu.

Hoisting to mechanizm silnika JavaScriptu, który podczas fazy kompilacji „wynosi” deklaracje zmiennych i funkcji na początek ich zakresu (scope), zanim kod zacznie się wykonywać linijka po linijce. W praktyce oznacza to, że możesz odwołać się do czegoś, co w kodzie zostało zadeklarowane dopiero niżej — a interpreter i tak będzie o tym wiedział. Słowo „wynoszenie” jest jednak metaforą: fizycznie nic się nie przesuwa, to silnik najpierw skanuje cały zakres i rejestruje deklaracje, a dopiero potem rusza z wykonaniem.

Jak to działa

JavaScript przetwarza kod w dwóch przejściach. W pierwszym (creation phase) rezerwuje pamięć dla wszystkich deklaracji znalezionych w danym zakresie. W drugim (execution phase) wykonuje instrukcje. Kluczowy szczegół: hoisting dotyczy deklaracji, nie przypisań. Zmienna zadeklarowana przez var jest wynoszona i od razu inicjowana wartością undefined, więc odwołanie do niej przed przypisaniem nie wyrzuci błędu — po prostu dostaniesz undefined.

Inaczej zachowują się let i const. Też są wynoszone, ale nie dostają wartości początkowej i lądują w tzw. Temporal Dead Zone (TDZ) — strefie od początku zakresu do linii deklaracji. Próba użycia ich w TDZ kończy się wyjątkiem ReferenceError. Z kolei deklaracje funkcji (function foo() {}) są wynoszone w całości, razem z ciałem, dlatego możesz je wywołać zanim pojawią się w kodzie.

Przykład z praktyki

Klasyczna pułapka, na którą trafi prawie każdy junior podczas code review w VS Code czy przy lintingu ESLintem:

  • console.log(x); var x = 5; wypisze undefined — deklaracja wyniesiona, przypisanie nie.
  • console.log(y); let y = 5; rzuci ReferenceError: Cannot access 'y' before initialization — przez TDZ.
  • powitaj(); przed function powitaj() {...} zadziała bez problemu.
  • Ale const powitaj = () => {} wywołane wcześniej już nie — bo function expression nie jest hoistowany jak deklaracja.

Częste błędy i mity

Najczęstszy mit brzmi: „kod fizycznie przeskakuje na górę”. Nie przeskakuje — silnik tylko wcześniej rejestruje nazwy. Drugi błąd to przekonanie, że let i const „nie są hoistowane”. Są — różnica polega na braku domyślnej inicjalizacji i obecności TDZ. Praktyczna rada: deklaruj zmienne na górze zakresu i używaj let/const zamiast var. Wtedy hoisting przestaje cię zaskakiwać, bo TDZ wymusza porządek, a narzędzia w stylu ESLinta wyłapią użycie przed deklaracją zanim trafi to na produkcję.

Pojęcia powiązane: scope (zasięg), Temporal Dead Zone, var/let/const, function declaration vs function expression, closure, lexical environment.