encyklopedia.space

Hoisting

Hoisting (dosł. „przenoszenie”) – mechanizm w językach programowania, w szczególności w JavaScript i innych implementacjach ECMAScript, polegający na „wywyższaniu” deklaracji zmiennych i funkcji na początek ich zakresu (zakresu) przed wykonaniem kodu. Dzięki temu elementy zadeklarowane w dalszej części skryptu mogą być już używane wcześniej, choć ich wartość zostanie zainicjowana dopiero w odpowiednim miejscu.

Historia

Mechanizm hoistingu został wprowadzony w początkowych wersjach JavaScript (1995‑1996), kiedy język był projektowany jako lekki skrypt do manipulacji stronami internetowymi. W późniejszych edycjach ECMAScript (ES5, ES6 i nowszych) zachowano tę koncepcję, jednocześnie wprowadzając nowe słowa kluczowe (let, const), które zachowują się inaczej pod względem hoistingu.

Jak działa hoisting?

Podczas fazy kompilacji (nie mylić z kompilacją do kodu maszynowego) interpreter JavaScript przetwarza cały kod źródłowy i segreguje go w dwie części:

  • deklaracje (zmiennych i funkcji) – które są „przenoszone” (hoisted) na początek ich zakresu,
  • inicjalizacje i wywołania – pozostają w miejscu ich rzeczywistego wystąpienia.

Hoisting zmiennych

W przypadku deklaracji przy użyciu var zmienna jest hoistowana, ale jej inicjalizacja nie jest. Przed pierwszą przypisaną wartością zmienna ma wartość undefined. Przykład:

console.log(a); // undefined – zmienna istnieje, ale nie jest jeszcze zainicjowana
var a = 5;
console.log(a); // 5

Hoisting funkcji

Pełne deklaracje funkcji (function foo() { … }) są hoistowane w całości, co pozwala wywołać funkcję przed jej fizycznym umiejscowieniem w kodzie:

foo(); // "Hello"
function foo() {
    console.log('Hello');
}

Natomiast wyrażenia funkcyjne (np. const foo = function() { … }) zachowują się jak zwykłe zmienne – ich nazwa jest hoistowana, ale wartość undefined powoduje błąd przy próbie wywołania.

let i const

Zmienne zadeklarowane przy użyciu let lub const są również hoistowane, ale znajdują się w tzw. Temporal Dead Zone (TDZ) – przed ich faktyczną deklaracją nie można uzyskać do nich dostępu; odwołanie się skutkuje ReferenceError:

console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 10;

Zakresy a hoisting

Hoisting jest ściśle powiązany z zakresem (ang. scope). W JavaScript istnieją dwa główne typy zakresów:

  • globalny – obejmuje cały skrypt lub plik;
  • funkcyjny – każdy funkcja tworzy własny zakres.

Zmienne zadeklarowane przy pomocy var są hoistowane do najbliższego zakresu funkcyjnego lub globalnego, natomiast let i const – do najbliższego bloku (block scope).

Praktyczne konsekwencje

Choć hoisting jest właściwością języka, zaleca się pisanie kodu w sposób, który minimalizuje jego efekty, aby uniknąć nieprzewidywalnych błędów i zwiększyć czytelność:

  • Używaj let i const zamiast var, co eliminuje nieoczekiwane undefined i TDZ;
  • Definiuj funkcje przed ich wywołaniem, chyba że potrzebujesz wzorców „funkcyjnych deklaracji”;
  • Unikaj mieszania deklaracji zmiennych i funkcji w tym samym bloku kodu.

Powiązane pojęcia

Przykłady typowych pułapek

// 1. Nawet w try…catch zmienna var jest hoistowana
try {
    console.log(x); // undefined, nie ReferenceError
} catch(e) {}
var x = 1;

// 2. Hoisting funkcji vs wyrażenia funkcyjne
foo();               // działa – deklaracja funkcji jest hoistowana
bar();               // ReferenceError – wyrażenie funkcyjne nie jest zainicjowane
function foo() { console.log('foo'); }
var bar = function() { console.log('bar'); };

Podsumowanie

Hoisting jest fundamentalnym mechanizmem w JavaScript, który wpływa na kolejność dostępności deklaracji zmiennych i funkcji. Zrozumienie jego działania jest niezbędne do pisania stabilnego i przewidywalnego kodu, a nowoczesne praktyki (użycie let, const, unikanie globalnych zmiennych) pozwalają zminimalizować ryzyko błędów związanych z tym mechanizmem.