03.14 Použití Closures
Výuka
Proč closures?
Už jsi viděl tento pattern několikrát v předchozích lekcích:
function makeCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = makeCounter();
console.log(counter()); // → 1
console.log(counter()); // → 2
console.log(counter()); // → 3
Jak je možné, že vnitřní funkce stále vidí count, i když makeCounter už skončila?
To je closure (uzávěr)! Funkce si "pamatuje" proměnné z vnějšího scope, i když ten scope už skončil.
Proč jsou closures užitečné?
- Soukromá data -
countnení dostupný zvenku, jen přes funkci - Zachování stavu - hodnota přežívá mezi voláními
- Factory pattern - vytváření funkcí s "nakonfigurovanými" daty
- Event handlers a callbacky - pamatují si kontext
Představ si to jako: Batoh na výlet. Funkce je jako turista, který si do batohu (closure) zabalil věci z domova (vnější scope). I když je daleko od domova (scope už skončil), má pořád k věcem přístup.
Jak to funguje?
Closure = funkce + prostředí (scope), ve kterém byla vytvořená:
1. Vytvoříš funkci UVNITŘ jiné funkce (nebo bloku)
2. Vnitřní funkce odkazuje na proměnné z vnějšího scope
3. Vrátíš nebo předáš tuto vnitřní funkci PRYČ (z vnějšího scope)
4. Když ji později zavoláš, stále vidí ty vnější proměnné
5. To jsou "closured" proměnné - closure si je "pamatuje"
Důležité:
- Closure není kopie proměnné, je to živé spojení (live link)
- Každá instance funkce má vlastní closure
- Closure funguje přes lexikální scope (kde je kód napsán)
Klíčové koncepty
- Closure - funkce si pamatuje proměnné z vnějšího scope
- Lexical scope + instance - closure je založen na tom, kde je funkce definovaná
- Live link - closure ukazuje na skutečnou proměnnou, ne kopii
- Private data - closure umožňuje data hiding
- Function factory - funkce, která vytváří jiné funkce s closures
JavaScript
Příklad 1: Základní closure - counter
function createCounter() {
let count = 0; // Soukromá proměnná
return function() {
count++; // Closure: vidí count z vnějšího scope!
return count;
};
}
const counterA = createCounter();
const counterB = createCounter();
console.log(counterA()); // → 1
console.log(counterA()); // → 2
console.log(counterB()); // → 1 (vlastní count!)
console.log(counterA()); // → 3
Co se stalo?
createCountervytvoří lokálnícount- Vrátí funkci, která "zavírá nad" (closes over)
count - Každé volání
createCountervytvoří novýcounta novou funkci counterAacounterBmají vlastní closures - vlastnícount- Closure si pamatuje
counti po tom, cocreateCounterskončí!
Příklad 2: Closure s parametrem
function makeMultiplier(factor) {
return function(number) {
return number * factor; // Closure: pamatuje si factor
};
}
const double = makeMultiplier(2);
const triple = makeMultiplier(3);
console.log(double(5)); // → 10 (5 * 2)
console.log(triple(5)); // → 15 (5 * 3)
console.log(double(10)); // → 20 (10 * 2)
Co se stalo?
doublesi "pamatuje"factor = 2triplesi "pamatuje"factor = 3- Každá instance má vlastní closure přes vlastní
factor
Příklad 3: Closure je live link, ne snapshot
function makeCounter() {
let count = 0;
return {
increment: () => ++count,
decrement: () => --count,
getValue: () => count
};
}
const counter = makeCounter();
console.log(counter.increment()); // → 1
console.log(counter.increment()); // → 2
console.log(counter.decrement()); // → 1
console.log(counter.getValue()); // → 1
Co se stalo?
- Všechny tři funkce sdílejí STEJNÝ
count(live link!) incrementzvýší count → všechny funkce vidí novou hodnotu- Není to kopie - je to odkaz na skutečnou proměnnou
Příklad 4: Soukromá data (data hiding)
function createBankAccount(initialBalance) {
let balance = initialBalance; // SOUKROMÉ - nedostupné zvenku!
return {
deposit(amount) {
if (amount > 0) {
balance += amount;
return balance;
}
return "Invalid amount";
},
withdraw(amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
return balance;
}
return "Invalid amount or insufficient funds";
},
getBalance() {
return balance;
}
};
}
const account = createBankAccount(100);
console.log(account.getBalance()); // → 100
console.log(account.deposit(50)); // → 150
console.log(account.withdraw(30)); // → 120
// ❌ Nelze přistoupit k balance přímo!
// console.log(account.balance); // → undefined
// balance = 9999999; // Nelze! balance je soukromý
Co se stalo?
balanceje soukromá - closure ji chrání- Jediný způsob přístupu jsou metody (
deposit,withdraw,getBalance) - Nemůžeš "podvádět" a měnit
balancepřímo - To je encapsulation (zapouzdření) - důležitý programming pattern!
Příklad 5: Closure v event handlerech
function setupButton(buttonId, message) {
const button = document.getElementById(buttonId);
button.addEventListener("click", function() {
console.log(message); // Closure: pamatuje si message!
});
}
setupButton("btn1", "Button 1 clicked!");
setupButton("btn2", "Button 2 clicked!");
// Když klikneš na btn1, vypíše: "Button 1 clicked!"
// Když klikneš na btn2, vypíše: "Button 2 clicked!"
Co se stalo?
- Každý event handler si pamatuje svůj vlastní
messagepřes closure - I když
setupButtonuž dávno skončila, handler stále vidímessage - To je velmi častý use case pro closures!
Příklad 6: Časté chyba - closure v cyklu s var
// ❌ ŠPATNĚ - všechny funkce sdílejí stejné i
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i); // Očekáváš 0, 1, 2
}, 1000);
}
// → 3, 3, 3 (všechny vidí konečné i = 3!)
// ✅ DOBŘE - let vytváří nový scope pro každou iteraci
for (let i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i); // Každá funkce má vlastní i
}, 1000);
}
// → 0, 1, 2 (každá funkce si pamatuje své i)
Co se stalo?
varmá function scope - JEDNOipro všechny iterace- Všechny closures ukazují na STEJNÉ
i - Když cyklus skončí,
i = 3→ všechny funkce vidí 3 letmá block scope - NOVÉipro každou iteraci- Každá closure má vlastní
i
TypeScript
TypeScript respektuje closures stejně jako JavaScript!
function createCounter(): () => number {
let count: number = 0;
return function(): number {
count++;
return count;
};
}
const counter: () => number = createCounter();
console.log(counter()); // → 1
Typy v closures:
function makeMultiplier(factor: number): (n: number) => number {
return (number: number): number => {
return number * factor; // Closure: factor je type number
};
}
const double: (n: number) => number = makeMultiplier(2);
console.log(double(5)); // → 10
TypeScript přidává:
- ✅ Typování closured proměnných -
factormá typnumber - ✅ Typování vrácených funkcí - jasně vidíš signaturu
- ✅ IntelliSense - editor ví, co closure obsahuje
Rozdíl JS vs TS
JavaScript:
function makeAdder(x) {
return function(y) {
return x + y; // x může být cokoli!
};
}
const add5 = makeAdder(5);
const addHello = makeAdder("hello");
console.log(add5(10)); // → 15
console.log(addHello(" world")); // → "hello world" (string concat!)
TypeScript:
function makeAdder(x: number): (y: number) => number {
return function(y: number): number {
return x + y;
};
}
const add5 = makeAdder(5);
const addHello = makeAdder("hello"); // ❌ Error: Argument of type 'string' is not assignable to parameter of type 'number'
console.log(add5(10)); // → 15
Rozdíl:
- JavaScript dovolí jakýkoliv typ v closure
- TypeScript kontroluje typy closured proměnných
- Bezpečnější - víš, co v closure máš
Tip
💡 Closure pro private data:
// ✅ Používej closure pro zapouzdření
function createUser(name, email) {
// Soukromá data
let password = null;
return {
getName: () => name,
getEmail: () => email,
setPassword: (newPassword) => {
password = newPassword; // Soukromé!
},
checkPassword: (inputPassword) => inputPassword === password
};
}
const user = createUser("Alice", "alice@example.com");
user.setPassword("secret123");
console.log(user.checkPassword("secret123")); // → true
// password není dostupný přímo!
💡 Používej let/const v cyklech pro closures:
// ❌ Špatně - var v cyklu
for (var i = 0; i < buttons.length; i++) {
buttons[i].onclick = function() {
console.log(i); // Všechny vidí konečné i!
};
}
// ✅ Dobře - let v cyklu
for (let i = 0; i < buttons.length; i++) {
buttons[i].onclick = function() {
console.log(i); // Každá má vlastní i
};
}
💡 Closure není magie - sleduj scope:
function outer() {
let x = 10;
function inner() {
console.log(x); // Closure: vidí x z outer
}
return inner;
}
const fn = outer();
fn(); // → 10
// Closure funguje díky lexical scope!
// inner má přístup k x, protože je definovaná uvnitř outer
Kvíz
Co vypíše tento kód?
function test() {
let x = 1;
setTimeout(function() {
console.log(x);
}, 1000);
x = 2;
}
test();
❌ - Closure NENÍ snapshot (kopie), je to live link
✅ - Closure ukazuje na skutečnou proměnnou x. Když se x změní na 2, closure vidí novou hodnotu
❌ - Closure si pamatuje proměnné i po skončení funkce
❌ - Closure zajišťuje, že x je stále dostupný
🎯 Závěrečný projekt
Po dokončení všech 8 dílů vytvoříte jednoduchou Todо aplikaci v čistém JavaScriptu. Naučíte se, jak aplikovat vše, co jste se naučili, na reálný projekt.
Zobrazit podrobnosti projektu →Informace o seriálu
Obtížnost
Délka
Cca 480 minut
Počet videí
8 videí + projekty
Certifikát
Po dokončení obdržíte certifikát
Lekce v této sekci
- 03.01 Deklarace funkce
- 03.02 Function Expression
- 03.03 Arrow Functions
- 03.04 Parametry a argumenty
- 03.05 Return hodnoty
- 03.06 Výchozí parametry
- 03.07 Rest parametry
- 03.08 Co je Scope
- 03.09 Lexikální Scope
- 03.10 Řetězec Scope
- 03.11 Globální Scope
- 03.12 Životní Cyklus Proměnných
- 03.13 Omezení Scope
- 03.14 Použití Closures
- 03.15 Callback funkce
- 03.16 Higher-order Functions
- 03.17 IIFE
- 03.18 Void funkce
- 03.19 Rekurze
Struktura lekcí (souborový strom)
- 1.1 Úvod do JavaScriptu a TypeScriptu
- 1.2 Nastavení prostředí
- 1.3 První program
- 1.4 Proměnné: var, let, const
- 1.5 Datové typy - přehled
- 1.6 String (řetězce)
- 1.7 Number (čísla)
- 1.8 Boolean (pravda/nepravda)
- 1.9 Null a Undefined
- 1.10 Type Inference vs Annotations
- 1.11 Aritmetické operátory
- 1.12 Porovnávací operátory
- 1.13 Logické operátory
- 1.14 Komentáře
- 1.15 Console metody
- 03.01 Deklarace funkce
- 03.02 Function Expression
- 03.03 Arrow Functions
- 03.04 Parametry a argumenty
- 03.05 Return hodnoty
- 03.06 Výchozí parametry
- 03.07 Rest parametry
- 03.08 Co je Scope
- 03.09 Lexikální Scope
- 03.10 Řetězec Scope
- 03.11 Globální Scope
- 03.12 Životní Cyklus Proměnných
- 03.13 Omezení Scope
- 03.14 Použití Closures
- 03.15 Callback funkce
- 03.16 Higher-order Functions
- 03.17 IIFE
- 03.18 Void funkce
- 03.19 Rekurze
- v přípravě
- v přípravě
- v přípravě
- v přípravě
- 01 — Co je Next.js
- 02 — Vytvoření projektu
- 03 — Struktura projektu (app/)
- 04 — Page komponenty (page.js / page.tsx)
- 05 — Layout komponenty (layout.js / layout.tsx)
- 06 — File-based routing
- 07 — Dynamické routy ([id]/page.js)
- 08 — Link komponenta (navigace)
- 09 — Image komponenta (next/image)
- 10 — Metadata (title, description, Open Graph)
- 11 — Loading UI (loading.js / loading.tsx)
- 12 — Error handling (error.js / error.tsx)
- 13 — Not Found (not-found.js / not-found.tsx)
- v přípravě
- v přípravě
- v přípravě