W Parku Jurajskim nawet najlepiej zaplanowane systemy mogą zawieść - dinozaur może wyrwać się z wybiegu, czujnik może przestać działać, a karmnik automatyczny może otrzymać nieprawidłowe dane. Właśnie dlatego każdy system bezpieczeństwa parku potrzebuje protokołów awaryjnych. W JavaScript takim protokołem jest mechanizm try/catch/finally.
Wyobraź sobie, że każda operacja w parku to potencjalne zagrożenie. Blok
try to strefa, w której wykonujemy ryzykowne operacje, a catch to ekipa ratunkowa, która wkracza, gdy coś pójdzie nie tak.1try {
2 // Strefa ryzykowna - próba otwarcia wybiegu
3 const dinoData = JSON.parse('{ niepoprawny JSON }');
4 console.log(dinoData.species);
5} catch (error) {
6 // Ekipa ratunkowa - obsługa błędu
7 console.log("ALARM! Błąd w systemie:", error.message);
8}
9console.log("System działa dalej - park nie został zamknięty!");Bez
try/catch nasz program zatrzymałby się całkowicie na błędzie - jakby cały park musiał zostać ewakuowany z powodu jednego wadliwego czujnika. Dzięki obsłudze błędów, program kontynuuje działanie po obsłużeniu problemu.finally to blok, który zawsze się wykonuje - niezależnie od tego, czy błąd wystąpił, czy nie. To jak procedura zamknięcia ogrodzenia, która musi się odbyć niezależnie od wyniku misji.1function feedDinosaur(dinoName, food) {
2 console.log("Otwieranie karmnika dla " + dinoName + "...");
3
4 try {
5 if (!food) {
6 throw new Error("Brak pożywienia w systemie!");
7 }
8 console.log("Podaję " + food + " dla " + dinoName);
9 } catch (error) {
10 console.log("ALARM: " + error.message);
11 } finally {
12 // ZAWSZE zamykamy karmnik - czy udało się nakarmić, czy nie
13 console.log("Zamykanie karmnika - bezpieczeństwo przede wszystkim!");
14 }
15}
16
17feedDinosaur("Rex", "mięso"); // Sukces + zamknięcie
18feedDinosaur("Rex", null); // Błąd + zamknięcie (finally zawsze działa!)JavaScript ma wbudowaną hierarchię błędów, podobnie jak park ma różne poziomy alertów dla różnych zagrożeń:
1// TypeError - operacja na niewłaściwym typie
2try {
3 const dino = null;
4 dino.roar(); // null nie ma metod!
5} catch (error) {
6 console.log(error instanceof TypeError); // true
7 console.log(error.message); // "Cannot read properties of null"
8}
9
10// RangeError - wartość poza zakresem
11try {
12 const dinoArray = new Array(-5); // Ujemna długość tablicy!
13} catch (error) {
14 console.log(error instanceof RangeError); // true
15}
16
17// ReferenceError - nieistniejąca zmienna
18try {
19 console.log(nieistniejacyDinozaur);
20} catch (error) {
21 console.log(error instanceof ReferenceError); // true
22}Słowo kluczowe
throw pozwala nam celowo wywołać błąd - jak ręczne włączenie alarmu w parku, gdy zauważymy problem, zanim automatyczne systemy go wykryją.1function checkEnclosureFence(voltage) {
2 if (voltage < 0) {
3 throw new RangeError("Napięcie nie może być ujemne: " + voltage);
4 }
5 if (voltage < 5000) {
6 throw new Error("ALARM: Napięcie ogrodzenia poniżej minimum! (" + voltage + "V)");
7 }
8 console.log("Ogrodzenie sprawne: " + voltage + "V");
9}
10
11try {
12 checkEnclosureFence(10000); // OK
13 checkEnclosureFence(3000); // Wyrzuci Error
14} catch (error) {
15 console.log("Wykryto problem:", error.message);
16}Czasami
catch łapie błąd, ale nie potrafi go obsłużyć - wtedy ponownie go wyrzuca (re-throw), żeby ktoś wyżej w łańcuchu dowodzenia mógł zareagować.1function processEnclosureData(data) {
2 try {
3 const parsed = JSON.parse(data);
4 if (!parsed.species) {
5 throw new Error("Brak gatunku w danych wybiegu");
6 }
7 return parsed;
8 } catch (error) {
9 if (error instanceof SyntaxError) {
10 // Umiemy obsłużyć - uszkodzone dane
11 console.log("Uszkodzone dane, używam domyślnych wartości");
12 return { species: "Unknown", status: "error" };
13 }
14 // Inny błąd - przekazujemy wyżej
15 throw error;
16 }
17}W Parku Jurajskim potrzebujemy specjalistycznych procedur awaryjnych dla różnych sytuacji. W JavaScript tworzymy własne klasy błędów rozszerzając klasę
Error:1class DinosaurEscapeError extends Error {
2 constructor(species, enclosure) {
3 super("ALARM: " + species + " uciekł z wybiegu " + enclosure + "!");
4 this.name = "DinosaurEscapeError";
5 this.species = species;
6 this.enclosure = enclosure;
7 }
8}
9
10class FeedingError extends Error {
11 constructor(dinoName, reason) {
12 super("Błąd karmienia " + dinoName + ": " + reason);
13 this.name = "FeedingError";
14 this.dinoName = dinoName;
15 }
16}
17
18class EnclosureVoltageError extends Error {
19 constructor(enclosure, voltage) {
20 super("Napięcie w wybiegu " + enclosure + ": " + voltage + "V (minimum 5000V)");
21 this.name = "EnclosureVoltageError";
22 this.enclosure = enclosure;
23 this.voltage = voltage;
24 }
25}
26
27// Użycie z selektywnym catch
28function parkSecurityCheck(enclosures) {
29 for (const enc of enclosures) {
30 try {
31 if (enc.voltage < 5000) {
32 throw new EnclosureVoltageError(enc.name, enc.voltage);
33 }
34 if (enc.dinoEscaped) {
35 throw new DinosaurEscapeError(enc.species, enc.name);
36 }
37 console.log("Wybieg " + enc.name + " - status OK");
38 } catch (error) {
39 if (error instanceof DinosaurEscapeError) {
40 console.log("CZERWONY ALERT: " + error.message);
41 console.log("Gatunek: " + error.species);
42 } else if (error instanceof EnclosureVoltageError) {
43 console.log("ŻÓŁTY ALERT: " + error.message);
44 } else {
45 throw error; // Nieznany błąd - re-throw
46 }
47 }
48 }
49}Własne klasy błędów pozwalają na precyzyjną obsługę - różne typy zagrożeń wymagają różnych procedur, tak jak w prawdziwym parku różne alarmy uruchamiają różne protokoły.
Te dwa operatory to jak inteligentne czujniki w parku - pomagają bezpiecznie poruszać się po danych, które mogą być niekompletne.
Operator
?. bezpiecznie odwołuje się do zagnieżdżonych właściwości. Jeśli jakakolwiek wartość po drodze jest null lub undefined, zamiast błędu zwraca undefined.1const dinoRecord = {
2 species: "Velociraptor",
3 tracker: {
4 lastLocation: "Sektor C",
5 gps: { lat: 23.5, lng: -80.2 }
6 }
7};
8
9// Bez optional chaining - ryzyko TypeError
10// const lat = dinoRecord.medical.lastCheckup.date; // TypeError!
11
12// Z optional chaining - bezpieczny dostęp
13const lat = dinoRecord.medical?.lastCheckup?.date; // undefined (bez błędu!)
14const gpsLat = dinoRecord.tracker?.gps?.lat; // 23.5
15
16// Działa też z metodami i indeksami
17const dinoCall = dinoRecord.sounds?.roar?.(); // undefined (metoda nie istnieje)
18const firstTag = dinoRecord.tags?.[0]; // undefined (brak tablicy)Operator
?? zwraca wartość domyślną, gdy lewa strona to null lub undefined. W odróżnieniu od ||, nie reaguje na 0, "" czy false.1// Problem z || - traktuje 0 i "" jako "puste"
2const dinoWeight = 0;
3console.log(dinoWeight || "Brak danych"); // "Brak danych" (źle! 0 to prawidłowa waga)
4console.log(dinoWeight ?? "Brak danych"); // 0 (poprawnie!)
5
6// Praktyczny przykład z danymi dinozaurów
7function getDinoInfo(dino) {
8 const name = dino?.name ?? "Nieznany gatunek";
9 const weight = dino?.weight ?? "Brak danych";
10 const isHealthy = dino?.isHealthy ?? false;
11 const speed = dino?.speed ?? 0;
12
13 return name + " | Waga: " + weight + "kg | Zdrowy: " + isHealthy + " | Prędkość: " + speed;
14}
15
16console.log(getDinoInfo({ name: "Rex", weight: 8000 }));
17console.log(getDinoInfo(null)); // Bezpieczne - nie wyrzuci błędu1const parkData = {
2 enclosures: {
3 "A-1": { species: "T-Rex", voltage: 10000 }
4 }
5};
6
7// Bezpieczne odczytanie danych z wartością domyślną
8const voltage = parkData.enclosures?.["B-2"]?.voltage ?? "Brak czujnika";
9console.log("Napięcie B-2:", voltage); // "Brak czujnika"
10
11const rexVoltage = parkData.enclosures?.["A-1"]?.voltage ?? 0;
12console.log("Napięcie A-1:", rexVoltage); // 10000Dobra strategia obsługi błędów w aplikacji to jak wielopoziomowy system bezpieczeństwa w Parku Jurajskim: