Witaj ponownie w Parku Jurajskim! Dzisiaj zajmiemy się jednym z fundamentalnych konceptów JavaScript - zasięgiem zmiennych. Zrozumienie zasięgu zmiennych jest kluczowe dla pisania wydajnego, bezpiecznego i przewidywalnego kodu, szczególnie w złożonych systemach, takich jak nasz park pełen dinozaurów.
Zasięg zmiennych (scope) określa, gdzie w kodzie dana zmienna jest dostępna - innymi słowy, z którego miejsca w kodzie możemy się do niej odwołać. Podobnie jak w Parku Jurajskim mamy strefy z różnymi poziomami dostępu (strefa dla zwiedzających, laboratorium tylko dla naukowców, zagrody wymagające specjalnych uprawnień), tak w JavaScript mamy różne zasięgi dostępu do zmiennych.
W JavaScript wyróżniamy trzy główne typy zasięgów:
Zmienne zdefiniowane poza jakąkolwiek funkcją lub blokiem kodu mają zasięg globalny. Oznacza to, że są dostępne w całym programie, w każdej funkcji i bloku kodu.
1// Zmienne globalne - dostępne wszędzie, jak publiczne informacje o parku
2const parkName = "Jurassic Park";
3let visitorCount = 0;
4var isOpenToPublic = true;
5
6function checkParkStatus() {
7 console.log(`Nazwa parku: ${parkName}`);
8 console.log(`Liczba odwiedzających: ${visitorCount}`);
9 console.log(`Otwarty dla zwiedzających: ${isOpenToPublic}`);
10}
11
12function updateVisitorCount(newCount) {
13 // Możemy modyfikować zmienną globalną z dowolnej funkcji
14 visitorCount = newCount;
15 console.log(`Zaktualizowano liczbę odwiedzających: ${visitorCount}`);
16}
17
18// Wywołanie funkcji korzystających z globalnych zmiennych
19checkParkStatus();
20updateVisitorCount(250);
21checkParkStatus();Należy być ostrożnym ze zmiennymi globalnymi, ponieważ:
Zmienne globalne w naszym parku to jak dokumenty dostępne dla wszystkich pracowników - czasami są niezbędne, ale zbyt wiele takich dokumentów może prowadzić do bałaganu i nieporozumień.
Zmienne zdefiniowane wewnątrz funkcji są dostępne tylko wewnątrz tej funkcji. Są to zmienne lokalne dla danej funkcji.
1function monitorDinosaur(dinoId) {
2 // Te zmienne mają zasięg funkcyjny - są dostępne tylko wewnątrz tej funkcji
3 const startTime = Date.now();
4 let status = "Monitorowanie...";
5 var sensorData = [];
6
7 // Możemy używać zmiennych wewnątrz funkcji
8 status = "Zbieranie danych...";
9 sensorData.push({ time: startTime, temperature: 37.5 });
10
11 console.log(`Status: ${status}`);
12 console.log(`Dane z czujników: ${JSON.stringify(sensorData)}`);
13
14 // Zwracamy wynik
15 return {
16 id: dinoId,
17 monitoringStarted: startTime,
18 lastStatus: status
19 };
20}
21
22// Wywołanie funkcji
23const monitoringResults = monitorDinosaur("T-Rex-01");
24
25// Poniższy kod spowodowałby błąd, ponieważ próbujemy uzyskać dostęp
26// do zmiennych o zasięgu funkcyjnym z zewnątrz funkcji
27// console.log(startTime); // ReferenceError
28// console.log(status); // ReferenceError
29// console.log(sensorData); // ReferenceError
30
31// Ale mamy dostęp do wartości zwracanej przez funkcję
32console.log(monitoringResults);Zasięg funkcyjny działa jak strefy zastrzeżone w parku - tylko upoważniony personel (kod wewnątrz funkcji) ma dostęp do informacji i zasobów w tej strefie.
Wszystkie trzy słowa kluczowe (
var, let, const) tworzą zmienne o zasięgu funkcyjnym, ale jest między nimi istotna różnica:var ma zasięg funkcyjny, ale nie blokowylet i const mają zarówno zasięg funkcyjny jak i blokowyZobaczmy to na przykładzie:
1function securityCheck() {
2 // Wszystkie te zmienne są dostępne wewnątrz całej funkcji
3 var securityCode = "A1B2C3";
4 let accessLevel = 5;
5 const securityChief = "Robert Muldoon";
6
7 if (accessLevel >= 5) {
8 var emergencyProtocol = "Code Red"; // zasięg funkcyjny!
9 let backupSystem = "Operational"; // zasięg blokowy
10 const safeRoom = "Bunker B"; // zasięg blokowy
11
12 console.log(securityCode); // A1B2C3 - dostępne
13 console.log(accessLevel); // 5 - dostępne
14 console.log(securityChief); // Robert Muldoon - dostępne
15 }
16
17 console.log(emergencyProtocol); // Code Red - dostępne mimo że zdefiniowane w bloku if
18 // console.log(backupSystem); // ReferenceError - zmienna ma zasięg blokowy
19 // console.log(safeRoom); // ReferenceError - zmienna ma zasięg blokowy
20}
21
22securityCheck();Zasięg blokowy został wprowadzony w ES6 (ES2015) wraz z nowymi słowami kluczowymi
let i const. Zmienne zdefiniowane za pomocą let i const wewnątrz bloku kodu (ograniczonego nawiasami klamrowymi {}) są dostępne tylko wewnątrz tego bloku.1function monitorDinosaurBehavior() {
2 let dangerLevel = 0;
3
4 // Blok if - tworzy nowy zasięg
5 if (true) {
6 // Zasięg blokowy - zmienne dostępne tylko wewnątrz tego bloku
7 let behavior = "Spokojne";
8 const vitals = { heartRate: 80, temperature: 38 };
9
10 // Modyfikacja zmiennej z zewnętrznego zasięgu
11 dangerLevel = behavior === "Agresywne" ? 5 : 1;
12
13 console.log(behavior); // Spokojne - dostępne
14 console.log(vitals); // { heartRate: 80, temperature: 38 } - dostępne
15 console.log(dangerLevel); // 1 - dostępne
16 }
17
18 console.log(dangerLevel); // 1 - dostępne bo zdefiniowane w szerszym zasięgu
19 // console.log(behavior); // ReferenceError - zmienna ma zasięg blokowy
20 // console.log(vitals); // ReferenceError - zmienna ma zasięg blokowy
21
22 // Blok for - również tworzy nowy zasięg
23 for (let i = 0; i < 5; i++) {
24 // 'i' ma zasięg blokowy, dostępne tylko wewnątrz pętli
25 const sensorId = `Sensor-${i}`;
26 console.log(sensorId);
27 }
28
29 // console.log(i); // ReferenceError - 'i' ma zasięg blokowy
30 // console.log(sensorId); // ReferenceError - zmienna ma zasięg blokowy
31}
32
33monitorDinosaurBehavior();Zasięg blokowy jest jak poszczególne zagrody w naszym parku - każda zagroda ma własne zasady i dostęp, a wyposażenie przypisane do jednej zagrody nie jest dostępne w innych zagrodach.
Teraz możemy lepiej zrozumieć różnice między tymi trzema słowami kluczowymi:
var:undefinedlet:const:1function demonstrateVariables() {
2 // var
3 var dinosaur = "T-Rex";
4 var dinosaur = "Velociraptor"; // OK - można redeklarować
5 dinosaur = "Triceratops"; // OK - można modyfikować
6
7 // let
8 let park = "Jurassic Park";
9 // let park = "Dino World"; // Błąd - nie można redeklarować
10 park = "Jurassic World"; // OK - można modyfikować
11
12 // const
13 const island = "Isla Nublar";
14 // const island = "Isla Sorna"; // Błąd - nie można redeklarować
15 // island = "Another Island"; // Błąd - nie można modyfikować
16
17 // Ale właściwości obiektów const mogą być modyfikowane
18 const parkSystem = {
19 mainPower: true,
20 backupGenerator: false
21 };
22
23 // To jest dozwolone
24 parkSystem.mainPower = false;
25 parkSystem.backupGenerator = true;
26
27 console.log(parkSystem); // { mainPower: false, backupGenerator: true }
28}
29
30demonstrateVariables();JavaScript wykorzystuje zasięg leksykalny, co oznacza, że funkcje mogą uzyskać dostęp do zmiennych zdefiniowanych w ich zewnętrznym zakresie.
1function initSecuritySystem() {
2 // Zmienne w zasięgu funkcji zewnętrznej
3 const systemName = "JurassicSafe";
4 let isActive = false;
5 let alertLevel = 0;
6
7 // Funkcja wewnętrzna ma dostęp do zmiennych z funkcji zewnętrznej
8 function activateSystem() {
9 isActive = true;
10 console.log(`System ${systemName} został aktywowany.`);
11 return true;
12 }
13
14 // Jeszcze jedna funkcja wewnętrzna
15 function setAlertLevel(level) {
16 alertLevel = level;
17 console.log(`System ${systemName}: poziom alertu ustawiony na ${alertLevel}`);
18 }
19
20 // Zwracamy funkcje wewnętrzne
21 return {
22 activate: activateSystem,
23 setAlert: setAlertLevel,
24 getStatus: function() {
25 return {
26 name: systemName,
27 active: isActive,
28 alertLevel: alertLevel
29 };
30 }
31 };
32}
33
34// Tworzymy instancję systemu bezpieczeństwa
35const securitySystem = initSecuritySystem();
36
37// Używamy zwróconych funkcji
38securitySystem.activate(); // System JurassicSafe został aktywowany.
39securitySystem.setAlert(3); // System JurassicSafe: poziom alertu ustawiony na 3
40console.log(securitySystem.getStatus()); // { name: "JurassicSafe", active: true, alertLevel: 3 }W powyższym przykładzie funkcje wewnętrzne tworzą domknięcie (closure) nad zmiennymi z funkcji
initSecuritySystem. Nawet po zakończeniu wykonania initSecuritySystem, zwrócone funkcje nadal mają dostęp do tych zmiennych.Domknięcia są jak zespoły bezpieczeństwa w parku - nawet gdy opuszczą centrum dowodzenia, nadal mają dostęp do kodów i protokołów, które zabrali ze sobą.
Stwórzmy bardziej kompleksowy przykład, który demonstruje różne zasięgi zmiennych w kontekście systemu zarządzania parkiem:
1// Zmienne globalne - dostępne w całym programie
2const PARK_NAME = "Jurassic Park";
3let parkStatus = "closed";
4var totalVisitors = 0;
5
6// Funkcja IIFE (Immediately Invoked Function Expression) tworząca moduł
7const parkSystem = (function() {
8 // Zmienne prywatne - zasięg funkcyjny, niedostępne spoza modułu
9 let securityStatus = "active";
10 let powerStatus = "online";
11 const MAX_CAPACITY = 2000;
12
13 // Prywatna funkcja pomocnicza
14 function checkSystemStatus() {
15 return powerStatus === "online" && securityStatus === "active";
16 }
17
18 // Publiczne API modułu
19 return {
20 // Metoda otwierająca park dla zwiedzających
21 openPark: function() {
22 if (checkSystemStatus()) {
23 parkStatus = "open"; // Modyfikacja zmiennej globalnej
24 console.log(`${PARK_NAME} jest teraz otwarty dla zwiedzających!`);
25 return true;
26 } else {
27 console.log("Nie można otworzyć parku - systemy bezpieczeństwa nie są w pełni operacyjne.");
28 return false;
29 }
30 },
31
32 // Metoda rejestrująca nowych odwiedzających
33 registerVisitors: function(count) {
34 // Sprawdzenie pojemności parku
35 if (parkStatus !== "open") {
36 console.log("Park jest zamknięty. Nie można zarejestrować odwiedzających.");
37 return false;
38 }
39
40 // Zmienna z zasięgiem blokowym
41 const availableCapacity = MAX_CAPACITY - totalVisitors;
42
43 if (count <= availableCapacity) {
44 totalVisitors += count; // Modyfikacja zmiennej globalnej
45 console.log(`Zarejestrowano ${count} nowych odwiedzających. Łącznie: ${totalVisitors}`);
46
47 // Zasięg blokowy wewnątrz warunku
48 if (totalVisitors > MAX_CAPACITY * 0.8) {
49 const warningMessage = "Uwaga: Park zbliża się do maksymalnej pojemności!";
50 console.log(warningMessage);
51 }
52
53 return true;
54 } else {
55 console.log(`Błąd: Za dużo odwiedzających. Dostępna pojemność: ${availableCapacity}`);
56 return false;
57 }
58 },
59
60 // Metoda zamykająca park
61 closePark: function() {
62 // Funkcja lokalna z dostępem do zmiennych z szerszego zakresu
63 function evacuateVisitors() {
64 console.log(`Ewakuowanie ${totalVisitors} odwiedzających z parku...`);
65 totalVisitors = 0;
66 }
67
68 parkStatus = "closed";
69 evacuateVisitors();
70 console.log(`${PARK_NAME} został zamknięty.`);
71
72 return true;
73 },
74
75 // Metoda zmieniająca status systemów
76 updateSystems: function(power, security) {
77 // Dla demonstracji let vs var
78 if (power) {
79 let localMessage = "Aktualizacja systemu zasilania...";
80 console.log(localMessage);
81 powerStatus = power;
82 }
83
84 if (security) {
85 var statusMessage = "Aktualizacja systemu bezpieczeństwa...";
86 console.log(statusMessage);
87 securityStatus = security;
88 }
89
90 // console.log(localMessage); // Błąd: localMessage ma zasięg blokowy
91 console.log(statusMessage); // Działa: statusMessage ma zasięg funkcyjny
92
93 return {
94 power: powerStatus,
95 security: securityStatus
96 };
97 }
98 };
99})();
100
101// Korzystanie z naszego systemu
102console.log(`Status parku przed otwarciem: ${parkStatus}`);
103parkSystem.openPark();
104console.log(`Status parku po otwarciu: ${parkStatus}`);
105
106parkSystem.registerVisitors(500);
107parkSystem.registerVisitors(1200); // Powinien pokazać ostrzeżenie o pojemności
108parkSystem.registerVisitors(400); // Powinien pokazać błąd o przekroczeniu pojemności
109
110// Aktualizacja systemów
111parkSystem.updateSystems("offline", null);
112parkSystem.openPark(); // Powinien pokazać błąd - nie można otworzyć parku
113
114// Przywrócenie systemów i zamknięcie parku
115parkSystem.updateSystems("online", "active");
116parkSystem.closePark();
117console.log(`Status parku po zamknięciu: ${parkStatus}`);
118console.log(`Liczba odwiedzających po ewakuacji: ${totalVisitors}`);
119
120// Próba dostępu do zmiennych prywatnych
121// console.log(securityStatus); // Błąd: zmienna niedostępna
122// console.log(MAX_CAPACITY); // Błąd: zmienna niedostępnaUnikaj zmiennych globalnych - podobnie jak w rzeczywistym parku, im więcej publicznych obszarów, tym trudniej je monitorować i zabezpieczyć.
Preferuj
nad const
, a let
nad let
- większość zmiennych nie powinna zmieniać swoich wartości, a jeśli już, to powinny mieć jak najmniejszy zasięg.var
Używaj zasięgu blokowego - ograniczaj widoczność zmiennych do najmniejszego możliwego zakresu.
Stosuj wzorzec modułu - enkapsuluj zmienne i funkcje w modułach, udostępniając tylko niezbędne API.
Bądź świadomy domknięć - domknięcia są potężnym narzędziem, ale mogą prowadzić do wycieków pamięci, jeśli są używane nieprawidłowo.
Zasięg zmiennych w JavaScript jest jak system stref dostępu w Parku Jurajskim:
Zrozumienie zasięgu zmiennych pozwala nam pisać lepiej zorganizowany, bardziej bezpieczny i przewidywalny kod - podobnie jak dobre zarządzanie strefami w parku pełnym niebezpiecznych dinozaurów zwiększa bezpieczeństwo wszystkich.
W następnym module zagłębimy się w temat hoistingu (wynoszenia), który jest ściśle związany z zasięgiem zmiennych i ma istotny wpływ na sposób działania kodu JavaScript.