W Parku Jurajskim każdy wybieg ma swój własny system zabezpieczeń - napięcie ogrodzenia, poziom alarmowy, licznik incydentów. Te dane są prywatne i niedostępne z zewnątrz, ale funkcje zarządzające wybiegiem mają do nich pełny dostęp. Dokładnie tak działają domknięcia (closures) - tworzą prywatne środowisko dla danych, dostępne tylko przez określone funkcje.
Domknięcie (closure) to funkcja, która "pamięta" zmienne z zakresu (scope), w którym została stworzona, nawet po tym, jak ten zakres przestał istnieć.
1function createCounter() {
2 let count = 0; // Zmienna prywatna - niedostepna z zewnatrz
3
4 return {
5 increment() { count++; return count; },
6 decrement() { count--; return count; },
7 getCount() { return count; },
8 };
9}
10
11const counter = createCounter();
12counter.increment(); // 1
13counter.increment(); // 2
14counter.getCount(); // 2
15// count jest calkowicie prywatna - nie ma do niej bezposredniego dostepuDomknięcia pozwalają tworzyć prawdziwie prywatne zmienne w JavaScript - to kluczowy wzorzec w programowaniu funkcyjnym:
1function createSecuritySystem(zoneName) {
2 let fenceVoltage = 10000;
3 let alertLevel = 0;
4 let incidentLog = [];
5
6 // Prywatna funkcja pomocnicza
7 function logEvent(event) {
8 incidentLog.push({ event, time: Date.now(), zone: zoneName });
9 }
10
11 return {
12 raiseFence() {
13 fenceVoltage += 2000;
14 logEvent('Fence voltage raised to ' + fenceVoltage);
15 return fenceVoltage;
16 },
17 triggerAlert() {
18 alertLevel++;
19 logEvent('Alert level: ' + alertLevel);
20 return alertLevel;
21 },
22 getStatus() {
23 return {
24 zone: zoneName,
25 voltage: fenceVoltage,
26 alert: alertLevel,
27 incidents: incidentLog.length,
28 };
29 },
30 };
31}
32
33const zoneA = createSecuritySystem('Zone A');
34const zoneB = createSecuritySystem('Zone B');
35
36zoneA.raiseFence(); // 12000
37zoneA.triggerAlert(); // 1
38zoneB.getStatus(); // { zone: 'Zone B', voltage: 10000, alert: 0, incidents: 0 }
39// Kazda strefa ma wlasne, izolowane daneDomknięcia są idealne do tworzenia fabryk - funkcji, które produkują wyspecjalizowane funkcje:
1// Fabryka walidatorow
2function createValidator(rules) {
3 return function validate(data) {
4 const errors = [];
5 for (const [field, rule] of Object.entries(rules)) {
6 if (rule.required && !data[field]) {
7 errors.push(field + ' is required');
8 }
9 if (rule.min && data[field] < rule.min) {
10 errors.push(field + ' must be >= ' + rule.min);
11 }
12 if (rule.max && data[field] > rule.max) {
13 errors.push(field + ' must be <= ' + rule.max);
14 }
15 }
16 return { valid: errors.length === 0, errors };
17 };
18}
19
20const validateDinosaur = createValidator({
21 name: { required: true },
22 weight: { required: true, min: 1, max: 100000 },
23 health: { required: true, min: 0, max: 100 },
24});
25
26validateDinosaur({ name: 'Rex', weight: 8000, health: 95 });
27// { valid: true, errors: [] }
28
29validateDinosaur({ name: '', weight: -5, health: 150 });
30// { valid: false, errors: ['name is required', 'weight must be >= 1', 'health must be <= 100'] }Memoizacja to technika cachowania wyników kosztownych obliczeń. Domknięcia zapewniają prywatny cache:
1function memoize(fn) {
2 const cache = new Map();
3
4 return function(...args) {
5 const key = JSON.stringify(args);
6 if (cache.has(key)) {
7 console.log('Cache hit for:', key);
8 return cache.get(key);
9 }
10 const result = fn(...args);
11 cache.set(key, result);
12 return result;
13 };
14}
15
16// Kosztowna analiza DNA
17function analyzeDNA(sampleId, depth) {
18 // Symulacja dlugiego obliczenia
19 let result = 0;
20 for (let i = 0; i < depth * 1000; i++) {
21 result += Math.random();
22 }
23 return { sampleId, score: result / (depth * 1000) };
24}
25
26const memoizedAnalyze = memoize(analyzeDNA);
27memoizedAnalyze('TREX-001', 100); // oblicza
28memoizedAnalyze('TREX-001', 100); // z cache - natychmiastowo!