Witaj z powrotem w Parku Jurajskim, @name! W poprzedniej lekcji sprawdzaliśmy, gdzie żyje zmienna, czyli jaki ma zasięg. Dzisiaj odpowiemy na pytanie równie ważne: od którego momentu zmienna albo funkcja w ogóle istnieje. Mechanizm, który o tym decyduje, nazywa się hoistingiem, a jego znajomość chroni przed najbardziej podstępną kategorią błędów w systemach parku - takich, które niczego nie wysadzają w powietrze, tylko po cichu podają złą wartość. Zmienna, która istnieje, ale nic nie zawiera, bywa groźniejsza niż zmienna, której nie ma wcale.
Hoisting (po polsku "wynoszenie") to zachowanie JavaScriptu polegające na przeniesieniu deklaracji zmiennych i funkcji na górę ich zasięgu - funkcyjnego albo globalnego - jeszcze w fazie kompilacji, zanim wykona się pierwsza linia kodu. Silnik najpierw przegląda cały kod i spisuje, jakie nazwy w nim występują, a dopiero potem zaczyna go wykonywać. Dzięki temu do części zmiennych i funkcji możemy odwołać się wcześniej, niż zostały zapisane w pliku.
To trochę jak spis inwentarza sporządzany, zanim dinozaury trafią do zagród: kompilator JavaScriptu najpierw notuje, "czego się spodziewać", a dopiero potem wykonuje konkretne operacje, linia po linii. Na górę wędrują wyłącznie deklaracje - nigdy wartości, które im przypisujemy.
Deklaracje funkcji wynoszone są w całości: razem z nazwą podróżuje też ciało funkcji. Możemy więc wywołać funkcję, zanim pojawi się w pliku. W zarządzaniu parkiem to bardzo wygodne, bo pozwala trzymać główną logikę na górze, a szczegóły techniczne spychać na dół.
1// Mozemy wywolac funkcje przed jej zdefiniowaniem
2checkSecurity("Zagroda T-Rex"); // "Sprawdzam bezpieczenstwo: Zagroda T-Rex"
3
4// Deklaracja funkcji jest hoistowana
5function checkSecurity(location) {
6 console.log(`Sprawdzam bezpieczenstwo: ${location}`);
7 return true;
8}Działa to dlatego, że w fazie kompilacji JavaScript wynosi na górę zasięgu całą deklarację - nazwę wraz z ciałem. Zanim wykona się pierwsza instrukcja,
checkSecurity jest już kompletną, gotową do wywołania funkcją, a nie pustym miejscem czekającym na uzupełnienie. To jedyny przypadek w całym języku, w którym na górę zasięgu trafia razem z nazwą także wartość, bo funkcja też jest wartością. Wszystkie pozostałe konstrukcje, które za chwilę zobaczymy, wynoszą samą nazwę i nic poza nią.W całości wynoszone są tylko deklaracje funkcji. Wyrażenia funkcyjne, czyli funkcje przypisane do zmiennej, podlegają regułom hoistingu zmiennych: sama zmienna trafia na górę, ale przypisana do niej funkcja już nie.
1// To NIE zadziala prawidlowo
2// feedDinosaur("Rex"); // TypeError: feedDinosaur is not a function
3
4// Hoistowana jest tylko deklaracja zmiennej, nie przypisanie funkcji
5var feedDinosaur = function(dinoName) {
6 console.log(`Karmienie dinozaura: ${dinoName}`);
7};
8
9// Od tej linii mozemy juz wywolac funkcje
10feedDinosaur("Rex"); // "Karmienie dinozaura: Rex"Przeczytaj uważnie komunikat błędu, bo to cenna wskazówka. Nie dostajemy
ReferenceError, tylko TypeError: feedDinosaur is not a function - nazwa feedDinosaur już istnieje, po prostu trzyma w tym momencie undefined, a undefined nie da się wywołać jak funkcji. Ta drobna różnica potrafi skrócić debugowanie z godziny do minuty: ReferenceError kieruje nas do literówki albo zapomnianego importu, a TypeError do kolejności linii w pliku. Deklaracja funkcji jest dostępna w całym swoim zasięgu, wyrażenie funkcyjne dopiero od linii z przypisaniem.Funkcje strzałkowe również są wyrażeniami, więc ich ciało nigdy nie jest wynoszone. Ponieważ przypisujemy je zwykle do
const albo let, błąd przy zbyt wczesnym wywołaniu okazuje się jeszcze bardziej stanowczy niż przy var.1// checkFence(); // ReferenceError: Cannot access checkFence before initialization
2
3const checkFence = () => "Ogrodzenie OK";
4
5console.log(checkFence()); // "Ogrodzenie OK"Różnica jest istotna: przy
var dostaliśmy TypeError, tutaj ReferenceError. Oba komunikaty znaczą to samo - "za wcześnie" - ale wersja z const zatrzymuje program dokładnie w miejscu pomyłki, zamiast puścić dalej wartość undefined, która wybuchnie kilkadziesiąt linii później, w zupełnie niewinnym fragmencie kodu. Stąd praktyczna reguła: przed linią definicji można używać wyłącznie funkcji zadeklarowanych przez function nazwa() {}. Wszystko, co przypisujemy do zmiennej - klasyczne wyrażenie funkcyjne czy strzałka - działa dopiero poniżej.Przy zmiennych hoisting zachowuje się różnie, zależnie od słowa kluczowego (
var, let, const). Stąd bierze się większość niespodzianek, więc rozłóżmy oba przypadki na czynniki pierwsze.Zmienne deklarowane przez
var są wynoszone i od razu inicjalizowane wartością undefined, więc odczyt przed deklaracją jest w pełni legalny:1console.log(dinosaurCount); // undefined (nie blad!)
2var dinosaurCount = 15;
3console.log(dinosaurCount); // 15Nic się nie zepsuło - i na tym właśnie polega problem. Program leci dalej z wartością, której nikt nie zaplanował, a
undefined w liczniku dinozaurów zamienia się w NaN w każdym obliczeniu, do którego trafi. Raport pokaże zero zwierząt w zagrodzie, w której siedzą trzy, i nikt nie dostanie żadnego ostrzeżenia. Łatwiej to zrozumieć, gdy wyobrazimy sobie, że przed uruchomieniem JavaScript przepisuje powyższy kod do takiej postaci:1var dinosaurCount; // Hoisting - deklaracja wedruje na gore
2console.log(dinosaurCount); // undefined
3dinosaurCount = 15; // Przypisanie zostaje tam, gdzie je napisalismy
4console.log(dinosaurCount); // 15Deklaracja wskoczyła na górę, przypisanie zostało dokładnie tam, gdzie je zapisaliśmy. Cała reguła hoistingu mieści się w jednym zdaniu: deklaracja wędruje w górę, wartość nie rusza się z miejsca. Odcinek między tymi dwoma punktami to okno, w którym zmienna jest osiągalna, ale pusta. Czujnik parku raportujący
undefined wygląda w logach niepokojąco podobnie do czujnika, który nie raportuje niczego - a to dwie zupełnie różne sytuacje, jedna oznacza spokój, druga zerwane ogrodzenie.Zmienne deklarowane przez
let i const (wprowadzone w ES6) także są hoistowane, ale nie dostają wartości undefined. Zamiast tego trafiają do temporal dead zone, czyli martwej strefy czasowej - okresu od rozpoczęcia wykonywania bloku aż do linii, w której zmienna faktycznie zostaje zadeklarowana. Sięgnięcie po nią w tym czasie kończy się błędem.1// To spowoduje blad:
2// console.log(securityLevel); // ReferenceError: Cannot access securityLevel before initialization
3let securityLevel = "Wysoki";
4console.log(securityLevel); // "Wysoki"
5
6// To samo dotyczy const:
7// console.log(parkName); // ReferenceError: Cannot access parkName before initialization
8const parkName = "Jurassic Park";
9console.log(parkName); // "Jurassic Park"Dwa komunikaty błędów trzeba umieć od siebie odróżnić:
ReferenceError: xyz is not definedlet lub const w temporal dead zone: ReferenceError: Cannot access 'xyz' before initializationTo rozróżnienie jest prezentem dla debugującego. Pierwszy przypadek oznacza, że zmiennej nie ma nigdzie - najpewniej literówka albo zapomniany import. Drugi mówi, że zmienna istnieje, tylko sięgnęliśmy po nią za wcześnie, więc wystarczy przenieść odczyt poniżej deklaracji. Wyobraź sobie funkcję na dwieście linii, w której wartość jest czytana na górze, a przypisywana na dole: z
var dostajemy ciche undefined i godzinę szukania po omacku, z let program zatrzymuje się natychmiast i wskazuje palcem konkretną linię. Temporal dead zone to nie ograniczenie, tylko system wczesnego ostrzegania.Klasy zachowują się jak
let i const, a nie jak deklaracje funkcji. Ich nazwa jest wynoszona, ale dopóki silnik nie wykona ciała klasy, każda próba użycia kończy się błędem.1// const dino = new Dinosaur("Rex"); // ReferenceError: Cannot access Dinosaur before initialization
2
3class Dinosaur {
4 constructor(name) {
5 this.name = name;
6 }
7}
8
9const dino = new Dinosaur("Rex"); // OK
10console.log(dino.name); // "Rex"Reguła "funkcje można definiować poniżej miejsca użycia" nie rozciąga się więc na klasy - klasa musi powstać przed pierwszym
new, które tworzy jej instancję. Jeśli z przyzwyczajenia zepchniesz definicję klasy na koniec pliku, to właśnie ten komunikat zobaczysz w konsoli, a lekarstwo jest banalne: przenieś klasę nad kod, który ją instancjonuje. W plikach parku daje to prostą kolejność - najpierw modele zwierząt i urządzeń, potem logika, która z nich korzysta, i nigdy odwrotnie.Hoisting jest ściśle spleciony z zasięgiem zmiennych, nazywanym też ich zakresem. Deklaracje nie wędrują na górę pliku, tylko na górę swojego zasięgu, a to, który zasięg jest "swój", zależy od słowa kluczowego:
var trzyma się zasięgu funkcyjnego, let i const - blokowego. W zasięgu globalnym reguła pozostaje ta sama, tyle że górą jest po prostu początek programu.var całkowicie ignoruje klamry if-a czy pętli: deklaracja ląduje na górze funkcji, która ją zawiera, niezależnie od tego, w którym miejscu ją zapisaliśmy.1function monitorSystem() {
2 console.log(status); // undefined (hoisting w obrebie funkcji)
3
4 if (true) {
5 var status = "Online";
6 }
7
8 console.log(status); // "Online"
9}
10
11monitorSystem();
12// console.log(status); // ReferenceError: status is not defined (poza zasiegiem funkcji)Blok
if w ogóle nie utworzył tutaj nowego zasięgu dla status. Deklaracja wskoczyła na górę monitorSystem, więc zmienną da się odczytać jeszcze przed blokiem (jest wtedy pusta), a po bloku trzyma już przypisaną wartość. Poza funkcją znika kompletnie, bo jedyną granicą, którą var respektuje, jest funkcja. Dwa console.log z tą samą nazwą dają zatem dwa różne wyniki - nie dlatego, że zmienne są dwie, tylko dlatego, że patrzymy na jedną zmienną raz przed przypisaniem, a raz po nim.Najlepszym poligonem do porównania obu zasięgów są pętle, bo licznik jest w nich deklarowany i używany w jednym miejscu. Sprawdź, co zostaje po pętli, w której licznik powstaje przez
var, a co po pętli z let.1for (var index = 0; index < 3; index++) {
2 // obchod zagrod
3}
4console.log(index); // 3 - var przezywa petle (zasieg funkcyjny)
5
6for (let step = 0; step < 3; step++) {
7 // obchod zagrod
8}
9// console.log(step); // ReferenceError - let zostaje w zasiegu blokowymPo zakończeniu pierwszej pętli
index nadal ma wartość 3, bo należy do zasięgu funkcyjnego i spokojnie przeżywa zamykającą klamrę. Zmienna step z drugiej pętli ma zasięg blokowy i znika razem z pętlą, więc odczyt poniżej kończy się błędem ReferenceError. Ta różnica stoi za słynnymi kłopotami z licznikiem w pętlach z opóźnieniem: przy var wszystkie wywołania widzą tę samą, końcową wartość, przy let każda iteracja dostaje własną kopię licznika i zachowuje swoją.Przy
let i const każda para klamer tworzy nowy zasięg, a temporal dead zone dotyczy właśnie tego bloku, a nie całej funkcji.1function checkEnclosures() {
2 // console.log(enclosureStatus); // ReferenceError (temporal dead zone)
3
4 if (true) {
5 let enclosureStatus = "Secure";
6 console.log(enclosureStatus); // "Secure"
7 }
8
9 // console.log(enclosureStatus); // ReferenceError (poza zasiegiem bloku)
10}
11
12checkEnclosures();Obie zakomentowane linie skończyłyby się błędem, ale z zupełnie różnych powodów: pierwsza dlatego, że zmienna siedzi jeszcze w temporal dead zone, druga dlatego, że blok
if się skończył i zmienna przestała istnieć. Klamry są tu prawdziwym murem i dokładnie o to nam chodzi - zmienna blokowa zostaje w swojej zagrodzie. W ćwiczeniu, które czeka Cię zaraz po tej lekcji, poćwiczysz zasięg zmiennych w różnych kontekstach: stworzysz zmienne z różnym zasięgiem - globalnym, funkcyjnym i blokowym - i będziesz obserwować ich zachowanie.Hoisting funkcji pozwala uporządkować plik według ważności, a nie według kolejności wykonania. Główna procedura idzie na górę, funkcje pomocnicze, z których korzysta, na sam dół - i wszystko nadal działa.
1// System monitorowania dinozaurow
2
3// Funkcje glowne sa zdefiniowane na gorze dla przejrzystosci
4function initMonitoringSystem() {
5 console.log("Inicjalizacja systemu monitorowania dinozaurow...");
6
7 // Mozemy wywolac te funkcje, choc sa zdefiniowane pozniej
8 const healthStatus = checkDinosaurHealth("Rex");
9 const locationData = trackDinosaur("Blue");
10
11 return {
12 health: healthStatus,
13 location: locationData,
14 systemStatus: "Online"
15 };
16}
17
18// Wywolanie glownej funkcji
19const systemStatus = initMonitoringSystem();
20console.log(systemStatus);
21
22// Funkcje pomocnicze zdefiniowane na dole pliku
23function checkDinosaurHealth(dinoName) {
24 console.log(`Sprawdzanie stanu zdrowia dinozaura: ${dinoName}`);
25 return "Zdrowy";
26}
27
28function trackDinosaur(dinoName) {
29 console.log(`Sledzenie dinozaura: ${dinoName}`);
30 return { x: 135, y: 270, area: "Sektor B" };
31}Wywołanie
initMonitoringSystem wypada w linii, do której żadna z funkcji pomocniczych jeszcze "nie doszła", a mimo to kod wykonuje się bez zgrzytu. To czysty hoisting deklaracji funkcji: obie były kompletne, zanim ruszyła pierwsza instrukcja. W czasie działania programu nic się już magicznie nie przesuwa, a zysk jest czysto ludzki - kto otworzy ten plik, najpierw przeczyta, co system robi, a dopiero potem, w jaki sposób.Ten sam mechanizm potrafi zadziałać przeciwko nam. Hoisting staje się zdradliwy, gdy funkcja wewnętrzna deklaruje zmienną o nazwie, która istnieje już w zasięgu zewnętrznym. Przeczytaj kod uważnie i spróbuj przewidzieć wynik, zanim zajrzysz do wyjaśnienia.
1// Pulapka zwiazana z hoistingiem
2
3// Funkcja sprawdzajaca zabezpieczenia
4function checkSecurity() {
5 var fenceStatus = "Offline"; // Hoistowana na gore funkcji
6
7 function enableFences() {
8 // Lokalna zmienna jest hoistowana na gore enableFences,
9 // ale az do linii inicjalizacji nie ma zadnej wartosci
10 console.log("Aktualny status plotu przed zmiana:", fenceStatus); // undefined!
11
12 // Lokalna zmienna fenceStatus przeslania te z wyzszego zakresu
13 var fenceStatus = "Online";
14 console.log("Nowy status plotu:", fenceStatus); // "Online"
15 }
16
17 enableFences();
18 console.log("Status plotu w glownej funkcji:", fenceStatus); // "Offline" (nie zmieniono)
19}
20
21checkSecurity();Kryje się tu subtelna pułapka:
console.log wewnątrz enableFences wypisuje undefined, ponieważ lokalna zmienna fenceStatus została wyniesiona na górę funkcji wewnętrznej i od pierwszej linii przesłania tę z zasięgu zewnętrznego, choć wartość dostaje dopiero niżej. Zewnętrzna zmienna również nigdy się nie zmienia - do końca ma wartość "Offline". W prawdziwym parku wygląda to tak, że panel operatora ogłasza włączone ogrodzenia, a główna funkcja bezpieczeństwa nadal uważa je za wyłączone.Zamiana
var na let i const likwiduje całą tę klasę problemów, bo zbyt wczesny odczyt zatrzymuje program, zamiast po cichu produkować undefined.1// Lepsze podejscie z uzyciem let i const
2
3function parkOperations() {
4 // Uzywamy const dla wartosci, ktore nie powinny sie zmieniac
5 const maxVisitors = 2000;
6
7 // Uzywamy let dla zmiennych, ktore beda sie zmieniac
8 let currentVisitors = 0;
9
10 function admitVisitors(count) {
11 // Przy let i const odczyt zmiennej przed deklaracja konczy sie bledem,
12 // co znacznie ulatwia wychwycenie problemu
13
14 // To by spowodowalo blad:
15 // console.log(availableSpace); // ReferenceError: Cannot access before initialization
16
17 // Najpierw deklarujemy, potem uzywamy
18 const availableSpace = maxVisitors - currentVisitors;
19 if (count <= availableSpace) {
20 currentVisitors += count;
21 console.log(`Przyjeto ${count} odwiedzajacych. Aktualnie w parku: ${currentVisitors}`);
22 return true;
23 }
24
25 console.log(`Za duzo odwiedzajacych! Dostepne miejsca: ${availableSpace}`);
26 return false;
27 }
28
29 // Testujemy nasza funkcje
30 admitVisitors(500); // Przyjeto 500 odwiedzajacych
31 admitVisitors(1000); // Przyjeto 1000 odwiedzajacych
32 admitVisitors(700); // Za duzo odwiedzajacych! Dostepne miejsca: 500
33
34 return currentVisitors;
35}
36
37console.log(`Laczna liczba odwiedzajacych: ${parkOperations()}`);Sprawdźmy, co się tu nie zmieniło:
maxVisitors i currentVisitors nadal są hoistowane, bo hoistingowi podlega każda deklaracja. Zmieniła się reakcja na pomyłkę - temporal dead zone przerabia zbyt wczesny odczyt na głośny błąd zamiast na dyskretne undefined. Każda wartość powstaje dokładnie tam, gdzie jest potrzebna, i ani chwili wcześniej, dzięki czemu logikę bramek czyta się z góry na dół, bez trzymania reguł hoistingu w głowie. O to właśnie chodzi w praktyce: nie o wykucie mechanizmu, tylko o pisanie kodu, w którym ten mechanizm nigdy nie zaskakuje.Zawsze deklaruj zmienne na górze ich zasięgu - to minimalizuje problemy związane z hoistingiem i sprawia, że kod czyta się dużo łatwiej.
Preferuj
i let
zamiast const
- błędy z temporal dead zone są nieporównanie łatwiejsze do zdiagnozowania niż tajemnicze wartości var
undefined, które ujawniają się kilkaset linii dalej.Deklaruj funkcje przed ich użyciem - hoisting funkcji działa, ale naturalna kolejność czytania i tak pozostaje najczytelniejsza dla osoby, która wróci do tego kodu za pół roku.
Unikaj deklaracji funkcji wewnątrz bloków warunkowych - zachowanie potrafi się różnić między środowiskami, więc bezpieczniej użyć wtedy wyrażenia funkcyjnego przypisanego do zmiennej.
Pamiętaj, że hoisting dotyczy deklaracji, a nie inicjalizacji - przypisanie wartości zachodzi dokładnie tam, gdzie je napisaliśmy, nigdy wcześniej.
Poniżej znajdziesz pełniejszy system zarządzania incydentami w Parku Jurajskim, który zbiera w jednym miejscu wszystko z tej lekcji: hoistowane deklaracje funkcji, zmienną
var na poziomie modułu oraz let i const użyte do stanu, który się zmienia, i do stanu, który zmieniać się nie może.1// System zarzadzania incydentami w Parku Jurajskim
2
3// Wykorzystanie hoistingu - deklarujemy zmienne na poczatku
4var activeIncidents = [];
5let alertLevel = "Normalny";
6const MAX_INCIDENTS = 5;
7
8// Glowna funkcja do inicjalizacji systemu
9function initIncidentSystem() {
10 // Ta zmienna jest hoistowana tylko w obrebie tej funkcji
11 var systemStatus = "Inicjalizacja";
12
13 // Wykorzystujemy hoisting funkcji - mozemy je wywolac przed definicja
14 logSystemStatus();
15 resetIncidents();
16
17 systemStatus = "Online";
18 logSystemStatus();
19
20 // Funkcja lokalna - widoczna tylko wewnatrz initIncidentSystem
21 function logSystemStatus() {
22 console.log(`Status systemu: ${systemStatus}`);
23 }
24
25 return {
26 reportIncident, // Referencja do funkcji zdefiniowanej ponizej
27 checkAlertLevel, // Funkcja zdefiniowana ponizej
28 getActiveIncidents, // Funkcja zdefiniowana ponizej
29 resetIncidents // Funkcja zdefiniowana wewnatrz initIncidentSystem
30 };
31
32 // Funkcja lokalna, ktora zwracamy jako czesc API
33 function resetIncidents() {
34 console.log("Resetowanie listy incydentow...");
35 activeIncidents = [];
36 updateAlertLevel();
37 }
38}
39
40// Deklaracje funkcji sa hoistowane, wiec mozna je wywolac przed definicja
41// Funkcja do zglaszania nowego incydentu
42function reportIncident(location, type, severity) {
43 console.log(`Zgloszenie incydentu: ${type} w lokalizacji ${location} (poziom zagrozenia: ${severity})`);
44
45 const incident = {
46 id: generateIncidentId(),
47 location,
48 type,
49 severity,
50 timestamp: new Date().toISOString(),
51 status: "Aktywny"
52 };
53
54 activeIncidents.push(incident);
55
56 if (activeIncidents.length > MAX_INCIDENTS) {
57 declareEmergency();
58 } else {
59 updateAlertLevel();
60 }
61
62 return incident;
63}
64
65// Generowanie unikalnego ID incydentu
66function generateIncidentId() {
67 return `INC-${Date.now()}-${Math.floor(Math.random() * 1000)}`;
68}
69
70// Aktualizacja poziomu alertu na podstawie aktywnych incydentow
71function updateAlertLevel() {
72 const highSeverityCount = activeIncidents.filter(inc => inc.severity === "Wysoki").length;
73
74 if (highSeverityCount >= 3) {
75 alertLevel = "Krytyczny";
76 } else if (highSeverityCount > 0 || activeIncidents.length >= 3) {
77 alertLevel = "Podwyzszony";
78 } else if (activeIncidents.length > 0) {
79 alertLevel = "Ostroznosc";
80 } else {
81 alertLevel = "Normalny";
82 }
83
84 console.log(`Poziom alertu zaktualizowany: ${alertLevel}`);
85}
86
87// Funkcja wywolywana w przypadku zbyt wielu incydentow
88function declareEmergency() {
89 alertLevel = "Ewakuacja";
90 console.log("UWAGA! Zbyt wiele aktywnych incydentow. Oglaszamy ewakuacje parku!");
91 // Kod do inicjacji protokolow ewakuacji...
92}
93
94// Funkcja do sprawdzania aktualnego poziomu alertu
95function checkAlertLevel() {
96 return {
97 level: alertLevel,
98 incidentCount: activeIncidents.length,
99 timestamp: new Date().toISOString()
100 };
101}
102
103// Funkcja zwracajaca liste aktywnych incydentow
104function getActiveIncidents() {
105 return [...activeIncidents]; // Zwracamy kopie, aby nikt nie zmodyfikowal oryginalu
106}
107
108// Inicjalizacja systemu
109const incidentSystem = initIncidentSystem();
110
111// Testowanie systemu
112console.log("===== TESTOWANIE SYSTEMU ZARZADZANIA INCYDENTAMI =====");
113
114// Sprawdzanie poczatkowego stanu
115console.log("Poczatkowy poziom alertu:", incidentSystem.checkAlertLevel().level);
116
117// Zglaszanie incydentow
118incidentSystem.reportIncident("Zagroda T-Rex", "Uszkodzenie ogrodzenia", "Wysoki");
119incidentSystem.reportIncident("Centrum Gosci", "Brak zasilania", "Sredni");
120
121// Sprawdzanie stanu po zgloszeniach
122console.log("Aktywne incydenty:", incidentSystem.getActiveIncidents().length);
123console.log("Aktualny poziom alertu:", incidentSystem.checkAlertLevel().level);
124
125// Dodawanie kolejnych incydentow wysokiego priorytetu
126incidentSystem.reportIncident("Laboratorium", "Wyciek materialu genetycznego", "Wysoki");
127incidentSystem.reportIncident("Zagroda Velociraptorow", "Proba ucieczki", "Wysoki");
128
129// Powinno wywolac stan ewakuacji
130incidentSystem.reportIncident("Sektor B", "Dinozaur poza zagroda", "Wysoki");
131incidentSystem.reportIncident("Centrum Operacyjne", "Awaria systemow bezpieczenstwa", "Wysoki");
132
133// Reset systemu
134incidentSystem.resetIncidents();
135console.log("Po resecie - poziom alertu:", incidentSystem.checkAlertLevel().level);Prześledź kolejność zdarzeń w tym pliku.
initIncidentSystem wywołuje logSystemStatus i resetIncidents, zanim którakolwiek z nich pojawi się w kodzie, a resetIncidents stoi nawet za instrukcją return - mimo to wszystko działa, bo deklaracje funkcji są kompletne przed wykonaniem pierwszej linii ciała. Ze zmiennymi jest inaczej: var systemStatus można odczytać od samego początku funkcji, ale wartość "Inicjalizacja" dostaje dopiero w swojej linii, a activeIncidents, alertLevel i MAX_INCIDENTS celowo trafiły na samą górę pliku, żeby nikt nie sięgnął po nie za wcześnie.Hoisting to ważny koncept JavaScriptu, który kształtuje sposób wykonywania naszego kodu:
Deklaracje funkcji są hoistowane w całości - można ich używać przed deklaracją.
Zmienne var są hoistowane i inicjalizowane wartością
undefined - można się do nich odwołać wcześniej, ale do momentu przypisania nie niosą żadnej użytecznej wartości.Zmienne let i const (a razem z nimi klasy) są hoistowane, ale zostają w temporal dead zone - próba odczytu przed deklaracją kończy się błędem.
Wyrażenia funkcyjne, w tym funkcje strzałkowe, podlegają regułom zmiennych - zmienna jest hoistowana, przypisana funkcja już nie.
Zrozumienie hoistingu jest kluczowe dla pisania przewidywalnego kodu JavaScript. Działa to dokładnie jak w samym Parku Jurajskim - znajomość zasad i protokołów wystarcza, żeby dzień minął bez niespodzianek i niebezpiecznych sytuacji.
"Hoisting w JavaScripcie przypomina poranne rozruchy parku" - mówi Dr. Rex. "Zanim przyjadą zwiedzający, zanim ruszy kod, część przygotowań dzieje się sama: protokoły są gotowe, procedury na miejscu. Ale prawdziwe dane trzeba dopiero zebrać na bieżąco, a sięganie po nie za wcześnie nie powie ci absolutnie nic!"
W kolejnej lekcji weźmiemy na warsztat słowo kluczowe
this - kolejny fundamentalny, a przy tym potrafiący nieźle namieszać element JavaScriptu.