W Parku Jurajskim bezpieczeństwo to priorytet numer jeden. Każdy wybieg ma swoje ogrodzenie, każde laboratorium - zamknięte drzwi z kodem dostępu. Podobnie działa zakres (scope) w JavaScript - to niewidzialne ogrodzenie, które kontroluje, które zmienne są dostępne w danym miejscu kodu. A closures (domknięcia) to jak dinozaur, który zapamiętuje kod PIN do swojego wybiegu, nawet gdy strażnik już dawno odszedł.
JavaScript używa zakresu leksykalnego - oznacza to, że dostęp do zmiennych jest określany przez miejsce, w którym kod został napisany, a nie gdzie został wywołany. Wyobraź sobie to jak mapę Parku Jurajskiego: każdy wybieg (funkcja) widzi główną drogę (zakres globalny) i swoje własne terytorium, ale nie widzi wnętrza innych wybiegów.
1// Zakres globalny - główna droga Parku Jurajskiego
2const parkName = "Jurassic Park";
3
4function enclosureA() {
5 // Zakres funkcji enclosureA - Wybieg A
6 const dinosaurA = "Tyrannosaurus Rex";
7
8 console.log(parkName); // "Jurassic Park" - widzi zakres globalny
9 console.log(dinosaurA); // "Tyrannosaurus Rex" - widzi swój zakres
10
11 function innerFence() {
12 // Zakres zagnieżdżony - wewnętrzne ogrodzenie
13 const babyDino = "Baby Rex";
14
15 console.log(parkName); // "Jurassic Park" - widzi zakres globalny
16 console.log(dinosaurA); // "Tyrannosaurus Rex" - widzi zakres rodzica
17 console.log(babyDino); // "Baby Rex" - widzi swój zakres
18 }
19
20 innerFence();
21 // console.log(babyDino); // BŁĄD! Nie widzi wnętrza innerFence
22}
23
24function enclosureB() {
25 // Zakres funkcji enclosureB - Wybieg B
26 const dinosaurB = "Triceratops";
27
28 console.log(parkName); // "Jurassic Park" - widzi zakres globalny
29 // console.log(dinosaurA); // BŁĄD! Nie widzi wnętrza enclosureA
30}Zasada jest prosta: funkcja wewnętrzna widzi zmienne z funkcji zewnętrznej, ale nie odwrotnie. Jak dinozaur w wewnętrznym wybiegu widzi przez ogrodzenie na zewnątrz, ale strażnik z zewnątrz nie widzi przez podwójne ściany do środka.
Closure to funkcja, która "pamięta" zmienne z zakresu, w którym została utworzona, nawet po tym, jak ten zakres przestał istnieć. To jak dinozaur, który zapamiętał drogę ucieczki - nawet gdy ogrodzenie zostanie przebudowane, on wciąż pamięta stary układ.
1function createDinoTracker(species) {
2 // Zmienna "species" jest w zakresie createDinoTracker
3 let sightings = 0;
4
5 // Ta funkcja wewnętrzna tworzy CLOSURE
6 // - "pamięta" species i sightings, nawet po zakończeniu createDinoTracker
7 return function reportSighting(location) {
8 sightings++;
9 console.log(species + " widziany w: " + location + " (obserwacja #" + sightings + ")");
10 };
11}
12
13// createDinoTracker zakończyło działanie, ale zwrócona funkcja PAMIĘTA zmienne
14const trackRex = createDinoTracker("T-Rex");
15const trackRaptor = createDinoTracker("Velociraptor");
16
17trackRex("Sektor A"); // "T-Rex widziany w: Sektor A (obserwacja #1)"
18trackRex("Sektor B"); // "T-Rex widziany w: Sektor B (obserwacja #2)"
19trackRaptor("Laboratorium"); // "Velociraptor widziany w: Laboratorium (obserwacja #1)"
20
21// Każde closure ma WŁASNĄ kopię zmiennych!
22// trackRex ma swoje sightings = 2, trackRaptor ma swoje sightings = 1Closures pozwalają tworzyć prywatne zmienne - dane, do których nie można dostać się z zewnątrz. Jak system bezpieczeństwa parku, gdzie kody dostępu są ukryte wewnątrz zamkniętego systemu.
1function createSecuritySystem(enclosureName) {
2 // Te zmienne są PRYWATNE - niedostępne z zewnątrz
3 let accessCode = Math.floor(Math.random() * 9000) + 1000;
4 let isLocked = true;
5 const accessLog = [];
6
7 return {
8 unlock(code) {
9 if (code === accessCode) {
10 isLocked = false;
11 accessLog.push("Odblokowano: " + new Date().toLocaleTimeString());
12 console.log(enclosureName + " - ogrodzenie WYŁĄCZONE");
13 } else {
14 accessLog.push("Nieudana próba: " + new Date().toLocaleTimeString());
15 console.log(enclosureName + " - BŁĘDNY KOD!");
16 }
17 },
18 lock() {
19 isLocked = true;
20 accessLog.push("Zablokowano: " + new Date().toLocaleTimeString());
21 console.log(enclosureName + " - ogrodzenie WŁĄCZONE");
22 },
23 getStatus() {
24 return isLocked ? "ZAMKNIĘTY" : "OTWARTY";
25 },
26 getLog() {
27 return [...accessLog]; // Zwraca kopię, nie oryginał
28 }
29 };
30}
31
32const rexSecurity = createSecuritySystem("Wybieg T-Rex");
33// Nie ma sposobu, by bezpośrednio odczytać accessCode!
34// rexSecurity.accessCode → undefined (prywatna zmienna)Closures umożliwiają tworzenie fabryk - funkcji, które produkują inne funkcje z wbudowaną konfiguracją. Jak laboratorium genetyczne, które tworzy dinozaury z określonym DNA.
1function createDinoFilter(property, value) {
2 // Closure "zapamiętuje" property i value
3 return function(dinosaur) {
4 return dinosaur[property] === value;
5 };
6}
7
8const isCarnivore = createDinoFilter("diet", "carnivore");
9const isHerbivore = createDinoFilter("diet", "herbivore");
10const isSectorA = createDinoFilter("sector", "A");
11
12const dinosaurs = [
13 { name: "Rex", diet: "carnivore", sector: "A" },
14 { name: "Tricia", diet: "herbivore", sector: "B" },
15 { name: "Blue", diet: "carnivore", sector: "C" }
16];
17
18console.log(dinosaurs.filter(isCarnivore)); // Rex, Blue
19console.log(dinosaurs.filter(isHerbivore)); // Tricia
20console.log(dinosaurs.filter(isSectorA)); // RexIIFE (natychmiast wywoływane wyrażenie funkcyjne) to funkcja, która jest definiowana i od razu wykonywana. To jak jednorazowy system bezpieczeństwa - uruchamia się, wykonuje swoje zadanie i znika, nie zostawiając śladów w zakresie globalnym.
1// Składnia IIFE
2(function() {
3 const secretData = "Lokalizacja ukrytego laboratorium";
4 console.log("IIFE wykonane! Dane: " + secretData);
5})();
6// secretData nie istnieje w zakresie globalnym!
7
8// IIFE ze strzałką
9(() => {
10 const emergencyProtocol = "Ewakuacja sektora 7";
11 console.log("Protokół awaryjny aktywowany: " + emergencyProtocol);
12})();
13
14// IIFE z parametrami
15const dinoReport = (function(parkData) {
16 const processed = parkData.map(d => d.toUpperCase());
17 return processed.join(", ");
18})(["rex", "raptor", "triceratops"]);
19
20console.log(dinoReport); // "REX, RAPTOR, TRICERATOPS"IIFE były bardzo popularne przed ES6, gdy
var nie miał zakresu blokowego. Dziś nadal przydają się do izolacji kodu i tworzenia modułów.Hoisting (wynoszenie) to mechanizm JavaScript, w którym deklaracje zmiennych i funkcji są "przenoszone" na początek swojego zakresu. Ale zachowują się różnie w zależności od słowa kluczowego!
1// var - hoisting z wartością undefined
2console.log(dinoVar); // undefined (nie błąd!)
3var dinoVar = "Stegosaurus";
4console.log(dinoVar); // "Stegosaurus"
5
6// let/const - hoisting ale z "Temporal Dead Zone" (TDZ)
7// console.log(dinoLet); // ReferenceError! Zmienna w TDZ
8let dinoLet = "Ankylosaurus";
9console.log(dinoLet); // "Ankylosaurus"
10
11// const - tak samo jak let, plus wymaga natychmiastowej inicjalizacji
12// console.log(dinoConst); // ReferenceError! TDZ
13const dinoConst = "Brachiosaurus";To jeden z najsłynniejszych problemów w JavaScript, gdzie closures i
var tworzą nieoczekiwane rezultaty:1// Problem z var - wszystkie closures dzielą tę samą zmienną
2for (var i = 0; i < 3; i++) {
3 setTimeout(function() {
4 console.log("var: Dinozaur #" + i); // Zawsze "Dinozaur #3"!
5 }, 100);
6}
7
8// Rozwiązanie z let - każda iteracja ma własną kopię
9for (let j = 0; j < 3; j++) {
10 setTimeout(function() {
11 console.log("let: Dinozaur #" + j); // 0, 1, 2 - poprawnie!
12 }, 100);
13}Dlaczego
var nie działa? Bo var ma zakres funkcyjny, więc wszystkie trzy closures odwołują się do tej samej zmiennej i, która po zakończeniu pętli ma wartość 3. let ma zakres blokowy - każda iteracja pętli tworzy nową zmienną j.| Koncept | Analogia w Parku Jurajskim | |---------|---------------------------| | Zakres globalny | Główna droga parku - widoczna ze wszystkich wybiegów | | Zakres funkcji | Wybieg dinozaura - widzi na zewnątrz, ale nikt nie widzi do środka | | Closure | Dinozaur pamięta układ parku nawet po przebudowie ogrodzeń | | Prywatne zmienne | Kod PIN do ogrodzenia - ukryty w zamkniętym systemie | | IIFE | Jednorazowy protokół bezpieczeństwa - wykonaj i zapomnij | | Hoisting var | Dinozaur, który pojawia się na liście przed wykluczeniem z jaja | | TDZ (let/const) | Strefa zakazu wstępu do momentu oficjalnego otwarcia wybiegu |