Objektově orientované programování v JavaScriptu
Prototype chain, klíčové slovo this, konstruktory, třídy (class), dědičnost, mixiny a kompozice. Pochopení toho, jak JS implementuje OOP jinak než klasické jazyky jako Java.
🔗 Prototype a prototypový řetězec
[[Prototype]] na jiný objekt (nebo null). Při přístupu k vlastnosti se JS postupně „dívá nahoru" po řetězci, dokud vlastnost nenajde nebo nenarazí na null.
JavaScript není class-based jazyk v klasickém smyslu – pod kapotou je to prototype-based dědičnost. Klíčové slovo class (ES6+) je pouze syntaktický cukr nad prototypy.
- Vlastní vlastnosti (own properties) – definovány přímo na objektu (
this.name = ...) - Zděděné vlastnosti (inherited properties) – přístupné přes
__proto__, ale nepatří přímo objektu __proto__– přímý přístup na prototype chain (zastaralé, preferujObject.getPrototypeOf()/Object.setPrototypeOf())
const obj1 = { a: 1 }
const obj2 = { b: 2 }
obj1.__proto__ = obj2
// `a` je vlastní vlastnost obj1
// `b` je dostupné přes __proto__ (zděděné)
Proč to tak je? JS byl navržen jako lehký, flexibilní jazyk. Místo pevné třídní hierarchie (jako Java) umožňuje objektům dědit přímo od jiných objektů – to je výkonnější a flexibilnější pro dynamické prostředí browseru.
Vyhledávání vlastnosti (property lookup)
- Existuje vlastnost jako own property na objektu?
- Má objekt
__proto__? → hledej na__proto__ - Má
__proto__také svůj__proto__? → pokračuj nahoru - Dosáhl jsi
null? → vlastnost neexistuje (undefined)
user.toString()
// 1. toString() na user? Ne
// 2. user.__proto__ = User.prototype → toString()? Ne
// 3. User.prototype.__proto__ = Object.prototype → toString()? ANO ✓
🏗️ Konstruktory, new a Factory.prototype
new. JS automaticky: (1) vytvoří prázdný objekt, (2) nastaví jeho [[Prototype]] na Factory.prototype, (3) přiřadí tento objekt jako this, (4) vrátí this (pokud funkce nevrátí jiný objekt).
Evoluce přístupu k vytváření objektů se sdílenými metodami:
function User(name) {
const user = {
name,
sayHi: function() { return this.name }
}
return user
}
// Pro 1000 uživatelů vznikne 1000 kopií sayHi() → plýtvání pamětí!
function User(name) {
// this = {}
// this.__proto__ = User.prototype
// this.constructor = User
this.name = name
// return this ← implicitně
}
User.prototype = {
sayHi: function() { return this.name }
}
const user = new User('Pepa')
Klíčový vztah: instance.__proto__ === Factory.prototype — to platí vždy, např. [].__proto__ === Array.prototype.
const commonMethods = { sayHi() { return this.name } }
function User(name) {
// Vytvoří objekt s daným prototypem, bez new
const user = Object.create(commonMethods, {
name: { value: name }
})
return user
}
// Problém: chybí .constructor, nefunguje instanceof správně
| Přístup | Výhody | Nevýhody |
|---|---|---|
| Kopírování metod do každého objektu | Jednoduchost | Paměťová neefektivita |
__proto__ mutace po vytvoření | Sdílení metod | Blokuje engine optimalizace (hidden classes) |
Object.create(proto) | Bez mutace, výkonné | Chybí constructor, broken instanceof |
Konstruktor + new + .prototype | Idiomatické, výkonné, instanceof funguje | Více boilerplate kódu |
class (ES6+) | Čistá syntax, private fields | Jen syntactic sugar – stále prototype pod kapotou |
👉 Klíčové slovo this
this – kontext volání funkce. Hodnota this není určena při definici funkce, ale při jejím volání. Výjimkou jsou arrow funkce, které this lexikálně přebírají z okolního scope.
Hodnota this závisí na způsobu volání funkce:
| Způsob volání | Hodnota this |
|---|---|
func() – přímé volání | undefined (strict mode) / globální objekt (window) |
obj.method() – metoda objektu | obj |
new Func() – konstruktor | nově vytvořený objekt |
func.call(ctx, ...args) | ctx (explicitně) |
func.apply(ctx, [args]) | ctx (explicitně) |
func.bind(ctx) | ctx (pevně navázáno) |
Arrow funkce () => {} | Lexikální this z okolního scope |
class Timer {
constructor() { this.count = 0 }
start() {
// Zde this = undefined nebo window, NE Timer instance!
setTimeout(function() { this.count++ }, 1000)
// Řešení 1: arrow funkce
setTimeout(() => { this.count++ }, 1000)
// Řešení 2: bind
setTimeout(function() { this.count++ }.bind(this), 1000)
}
}
Proč arrow funkce nemá vlastní this? Arrow funkce byly zavedeny v ES6 právě proto, aby vyřešily problém se ztrátou kontextu v callbackech. Jejich this je zachyceno lexikálně v okamžiku definice.
🧬 Prototypová dědičnost (bez class)
Pro zdědění vlastních vlastností i prototypálních metod je potřeba kombinovat dva přístupy:
FootballPlayer.prototype = Human.prototype
FootballPlayer.prototype.sayTeam = function() { return this.team }
// ❌ Přepíše i Human.prototype! Všechny Human instance by získaly sayTeam.
function Human(name) {
this.name = name
}
Human.prototype = {
sayName() { return this.name }
}
function FootballPlayer(name, team) {
Human.call(this, name) // ← zdědění VLASTNÍCH vlastností
this.team = team
}
// Nový objekt s Human.prototype jako __proto__, mutace se nešíří nahoru
FootballPlayer.prototype = Object.create(Human.prototype)
FootballPlayer.prototype.sayTeam = function() { return this.team }
Proč Object.create a ne new Human()? new Human() by spustilo konstruktor Human (nežádoucí vedlejší efekty). Object.create(Human.prototype) vytvoří objekt s daným prototypem bez spuštění konstruktoru.
Object.create = Object.create || function(proto) {
const F = function() {}
F.prototype = proto
return new F()
// Vytvoří prázdný objekt, jehož __proto__ je proto
}
Shrnutí kroků pro správnou prototypovou dědičnost:
- Vlastní vlastnosti:
ParentConstructor.call(this, ...args)uvnitř child konstruktoru - Prototypální metody:
Child.prototype = Object.create(Parent.prototype) - Přidat child-specifické metody:
Child.prototype.newMethod = ...
🏛️ Syntaxe class (ES6+)
class – syntaktický cukr nad prototypovou dědičností. typeof Human === 'function' – třídy jsou stále funkce. Metody definované uvnitř class jsou přidány na ClassName.prototype, ne přímo na instance.
class Human {
constructor(name) { this.name = name }
sayName() { return this.name }
}
class FootballPlayer extends Human {
constructor(name, team) {
super(name) // ≡ Human.call(this, name)
this.team = team
}
sayTeam() { return this.team }
}
// extends Human ≡ Object.create(Human.prototype)
// super(name) ≡ Human.call(this, name)
// Metody jsou stále na Human.prototype / FootballPlayer.prototype
Klíčová pravidla pro class:
super()musí být voláno v child konstruktoru před použitímthis- Třídy nemají hoisting jako funkce (temporal dead zone)
classtělo je vždy ve strict modetypeof MyClass === 'function'– je to funkce!
Private fields (#)
#fieldName) – vlastnosti přístupné pouze uvnitř dané třídy. Nejde je číst ani nastavit zvenčí (ani z odvozených tříd). Jsou skutečně privátní na úrovni jazyka, na rozdíl od konvence _name.
class Human {
#age
constructor() { this.#age = 30 }
isAdult() { return this.#age > 18 }
}
const user = new Human()
console.log(user.isAdult()) // true
console.log(user.#age) // ❌ SyntaxError
🧩 Mixiny a kompozice
Existují dva hlavní přístupy:
1. Class mixins (funkce vracející třídu)
const WithTimestamps = Base => class extends Base {
constructor(...args) {
super(...args)
this.createdAt = new Date()
this.updatedAt = new Date()
}
touch() { this.updatedAt = new Date() }
}
const WithId = Base => class extends Base {
constructor(...args) {
super(...args)
this.id = crypto.randomUUID()
}
}
class Model {}
class User extends WithTimestamps(WithId(Model)) {
constructor(name) { super(); this.name = name }
}
const u = new User("Petr")
u.touch()
2. Kompozice objektů (bez dědičnosti)
const asNamed = name => ({ name })
const asTimestamps = () => {
let createdAt = new Date(), updatedAt = new Date()
return {
get createdAt() { return createdAt },
get updatedAt() { return updatedAt },
touch() { updatedAt = new Date() }
}
}
const createUser = name => Object.assign({}, asNamed(name), asTimestamps())
const user = createUser('Petr')
user.touch()
Dědičnost: „is-a" vztah (FootballPlayer is-a Human). Vhodná pro jasné taxonomie.
Kompozice: „has-a" vztah (User has Timestamps, has Id). Flexibilnější, méně provázaná.
Obecné doporučení: „Prefer composition over inheritance" – kompozice je obvykle čitelnější a lépe rozšiřitelná.
🧊 Kopírování objektů a jejich zmrazení
structuredClone()
structuredClone(obj) – vytvoří hlubokou kopii objektu. Kopíruje pouze vlastní, enumerable vlastnosti. Nesmí obsahovat funkce (vyhodí chybu). Nezdědí nic z prototype chain.
const o1 = { a: 1 }
const o2 = Object.create(o1, {
b: { value: 2, enumerable: true }, // ✅ bude zkopírováno
c: { value: 3, enumerable: false }, // ❌ non-enumerable → ne
f: { value: () => {}, enumerable: false } // ❌ funkce → chyba pokud enumerable
})
const copy = structuredClone(o2)
console.log(copy) // { b: 2 }
// `a` chybí (inherited), `c` a `f` chybí (non-enumerable / funkce)
Object.freeze / seal / preventExtensions
| Metoda | Blokuje přidání props | Blokuje změnu props | Blokuje konfiguraci props | Blokuje změnu proto |
|---|---|---|---|---|
preventExtensions() | ✅ | ❌ | ❌ | ✅ |
seal() | ✅ | ❌ | ✅ | ✅ |
freeze() | ✅ | ✅ | ✅ | ✅ |
Poznámka: všechny tyto metody jsou mělké (shallow) – vnořené objekty zůstávají modifikovatelné.
Proxy objects
Proxy – wrapper kolem objektu, který umožňuje zachytit (interceptovat) operace jako čtení, zápis, volání funkcí, in operátor, atd. pomocí tzv. traps (get, set, has, deleteProperty, ...).
const handler = {
get(target, prop) {
console.log(`Čtení vlastnosti: ${prop}`)
return target[prop]
}
}
const proxy = new Proxy({ name: 'Pepa' }, handler)
proxy.name // → "Čtení vlastnosti: name"
🔍 Iterace přes vlastnosti a výkonnostní optimalizace
Získání všech vlastností (včetně zděděných)
// Imperativní styl
function getAllProps(obj) {
const props = Object.getOwnPropertyNames(obj)
while (obj = Object.getPrototypeOf(obj)) {
props.push(...Object.getOwnPropertyNames(obj))
}
return props
}
// Funkcionální styl (rekurzivní)
function getAllProps(obj) {
const proto = Object.getPrototypeOf(obj)
const props = Object.getOwnPropertyNames(obj)
return proto ? [...props, ...getAllProps(proto)] : props
}
| Metoda | Co vrací |
|---|---|
Object.keys(obj) | Vlastní enumerable string klíče |
Object.getOwnPropertyNames(obj) | Vlastní string klíče (i non-enumerable) |
for...in | Všechny enumerable klíče (i zděděné) |
Object.getPrototypeOf(obj) | Odkaz na prototype objektu |
Hidden Classes (skryté třídy)
Proto se nedoporučuje:
- Dynamicky přidávat vlastnosti objektu po vytvoření v různém pořadí
- Mutovat
__proto__po vytvoření objektu - Mazat vlastnosti (
delete obj.prop) – způsobí přechod na pomalejší hidden class
📋 Shrnutí okruhu 1
- JS používá prototypovou dědičnost – objekty dědí od objektů přes
__proto__řetězec. classje pouze syntaktický cukr – metody jsou naClass.prototype, ne na instancích.thiszávisí na způsobu volání, ne na místě definice; arrow funkce přebíráthislexikálně.- Pro správnou dědičnost bez
class:Parent.call(this)+Object.create(Parent.prototype). - Mutace
__proto__po vytvoření objektu blokuje engine optimalizace (hidden classes).
Kontrolní otázky a odpovědi
-
Co je prototype chain a jak JS vyhledává vlastnosti?
Každý objekt má interní
[[Prototype]]. Při přístupu ke vlastnosti JS nejprve hledá na objektu samotném, pak postupuje nahoru po řetězci__proto__, dokud nenarazí nanull. Pokud vlastnost nenajde, vrátíundefined. -
Jak se liší
classod prototypového přístupu?classje syntaktický cukr –typeof MyClass === 'function'. Metody třídy jsou přidány naMyClass.prototype.extendsodpovídáObject.create(Parent.prototype),super()odpovídáParent.call(this, ...args). -
Kdy má
thisjakou hodnotu?Závisí na způsobu volání: přímé volání →
undefined(strict) / globální objekt; metoda objektu → objekt;new→ nová instance;.call/.apply/.bind→ explicitně předaný kontext; arrow funkce → lexikálníthisz okolního scope. -
Jak správně implementovat dědičnost bez
class?Pro vlastní vlastnosti:
Parent.call(this, args)v konstruktoru potomka. Pro prototypální metody:Child.prototype = Object.create(Parent.prototype). NikdyChild.prototype = Parent.prototype– mutace by se šířily nahoru. -
Co jsou private fields a jak se liší od konvence
_name?Private fields (
#field) jsou skutečně privátní na úrovni jazyka – nelze k nim přistoupit zvenčí ani reflexí. Konvence_nameje pouze domluvá vývojářů, ale vlastnost je stále veřejná. -
Co jsou hidden classes a proč záleží na pořadí přidávání vlastností?
JS engine přiřazuje objektům interní „hidden class" popisující jejich strukturu. Objekty se stejnou strukturou sdílí hidden class a engine může optimalizovat přístup. Mutace
__proto__nebo měnící se struktura objektu způsobí de-optimalizaci.
Asynchronní programování v JavaScriptu
Event loop, call stack, fronty úloh (micro/macro queue), callbacky, Promises, async/await, generátory, AbortController, debounce/throttle a asynchronní iterátory.
⚙️ Asynchronicita vs. multithreading
JavaScript sám běží v jednom vlákně – v daný okamžik se provádí vždy jen jeden kus kódu. Asynchronicita NENÍ multithreading. JS deleguje pomalé operace na runtime (browser/Node.js), který může využít vlákna, a po dokončení operace zařadí callback do fronty.
🔄 Event Loop – srdce JS asynchronicity
Komponenty systému:
- Call Stack (zásobník volání) – LIFO struktura uchovávající prováděné funkce. Každé volání funkce přidá frame na zásobník, návrat ho odebere.
- Web APIs / Node.js APIs – prostředí runtime obsluhující asynchronní operace (setTimeout, fetch, DOM events, fs.readFile...). Mohou využívat interně vlákna.
- Callback Queue / Task Queue (macro queue) – fronta callbacků čekajících na zpracování (z setTimeout, DOM events, fetch...).
- Microtask Queue – prioritní fronta pro mikrotasky (Promise.then, queueMicrotask, MutationObserver).
- Event Loop – vybírá úlohy z front a vkládá je do call stacku.
- Proveď aktuální synchronní kód (call stack).
- Zpracuj všechny microtasky z microtask queue (včetně těch nově přidaných).
- Proveď jeden macrotask z macro queue (callback queue).
- Znovu zpracuj všechny microtasky.
- Příp. proveď render (v browseru).
- Opakuj.
Micro queue vs. Macro queue
| Micro queue (vyšší priorita) | Macro queue (nižší priorita) |
|---|---|
Promise.then / .catch / .finally | setTimeout(...) |
queueMicrotask(...) | setInterval(...) |
MutationObserver | MessageChannel |
process.nextTick() (Node.js, nejprioritnější) | window.postMessage(...) |
| – | DOM events (click, keydown, load...) |
| – | Network I/O (fetch/XHR events, WebSocket) |
| – | requestAnimationFrame(...) |
| – | requestIdleCallback(...) |
process.nextTick() je specifické pro Node.js a má nejvyšší prioritu – je zpracováno ještě před ostatními microtasky. Pokud se zavolá rekurzivně, může zablokovat zpracování I/O.
requestAnimationFrame a requestIdleCallback
requestAnimationFrame(cb)– spustí callback před dalším překreslením obrazovky (typicky 60× za sekundu). Ideální pro animace – eliminuje „trhání" oprotisetTimeout.requestIdleCallback(cb)– spustí callback v době, kdy browser nemá jinou práci (idle time). Vhodné pro méně prioritní úkoly (analytika, prefetching). Negarantuje čas spuštění.
😱 Callback Hell – problém klasických callbacků
getUser(id, (err, user) => {
if (err) return handleError(err)
getSettings(user.id, (err, settings) => {
if (err) return handleError(err)
fetchData(settings.endpoint, (err, data) => {
if (err) return handleError(err)
transform(data, (err, result) => {
// ... stále hlubší a hlubší vnoření → callback hell
})
})
})
})
Problémy: nečitelnost, opakované if (err) return handleError(err), složité sdílení proměnných mezi úrovněmi.
🤝 Promise – objekt zapouzdřující asynchronní hodnotu
pending (čeká), fulfilled (splněn s hodnotou), rejected (odmítnut s chybou). Stav je neměnný – jakmile je Promise fulfilled/rejected, nelze to změnit.
Interní struktura Promise:
Promise {
[[State]] = "pending" | "fulfilled" | "rejected"
[[Result]] = undefined | hodnota | error
[[FulfillQ]] = [] // fronty callbacků
[[RejectQ]] = []
}
Vytvoření Promise
function get(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) resolve(xhr.responseText)
else reject(xhr.status)
}
}
xhr.send()
})
}
Promise chaining (.then, .catch, .finally)
get('/api/user')
.then(user => get(`/api/permissions?uid=${user.id}`))
.then(perms => get(`/api/token?scopes=${perms.scopes.join(",")}`))
.then(token => get(`/api/report?token=${token.token}`))
.then(report => console.log("done:", report))
.catch(err => console.error('chyba:', err))
.finally(() => console.log('vždy se spustí'))
Proč vrácením Promise z .then dostaneme chain? Metoda .then() vždy vrátí nový Promise. Pokud callback vrátí Promise, nový Promise „přijme" jeho výsledek (unwrapping). Tím vzniká lineární chain místo vnoření.
Statické metody Promise
| Metoda | Chování | Kdy použít |
|---|---|---|
Promise.resolve(val) | Vrátí splněný Promise s hodnotou | Zabalení synchronní hodnoty |
Promise.reject(err) | Vrátí odmítnutý Promise s chybou | Rychlé odmítnutí |
Promise.all([...]) | Čeká na všechny; odmítne při první chybě | Paralelní operace, potřebuju všechny výsledky |
Promise.allSettled([...]) | Čeká na všechny, i chybné; vrátí status+value | Potřebuju výsledky i chybné operace |
Promise.any([...]) | Splní se při první úspěšné; chybí pokud všechny selžou | Race – chci první úspěch |
Promise.race([...]) | Splní/odmítne při první dokončené (i chybné) | Timeout pattern |
// Problém: user je mimo scope v pozdějším .then()
const userP = get('/api/user')
const permsP = userP.then(user => get(`/api/permissions?uid=${user.id}`))
const tokenP = Promise.all([userP, permsP]).then(([user, perms]) =>
get(`/api/token?uid=${user.id}&scopes=${perms.scopes.join(",")}`)
)
Promise.all([userP, permsP, tokenP]).then(([user, perms, token]) => {
console.log("done:", { user, perms, token })
})
⚡ Generátory – „líné" funkce s yield
function*), která může svůj výpočet pozastavit (yield) a předat kontrolu volajícímu. Vrátí iterator objekt s metodami next(value), return(value), throw(error).
Klíčové vlastnosti:
- Funkce
function*při zavolání okamžitě nespustí tělo – vrátí iterator. - Každé volání
generator.next(value)pokračuje až do dalšíhoyield. - Hodnota předaná do
next(value)se stane hodnotou výrazuyielduvnitř funkce. - Generátor je dokončen, když dosáhne
returnnebo konce funkce ({ value: ..., done: true }).
function* counter() {
let i = 0
while (true) {
const reset = yield i // pozastavit, předat i volajícímu
if (reset) i = 0
else i++
}
}
const gen = counter()
gen.next() // { value: 0, done: false }
gen.next() // { value: 1, done: false }
gen.next(true) // { value: 0, done: false } – reset!
Generátory jako základ pro async/await
Historicky byl async/await implementován pomocí generátorů a „runneru". Generátor pozastaví výpočet u yield promise, runner počká na splnění promise a výsledek předá zpět přes next(result):
function* main() {
const status = yield fetch('https://api.example.com/data')
const json = yield status.json()
console.log(json)
}
function run(gen) {
const g = gen()
;(function loop(value) {
const next = g.next(value)
if (!next.done) next.value.then(result => loop(result))
})()
}
run(main)
✨ async / await – syntaktický cukr nad Promises
async function – funkce, která vždy vrátí Promise. I pokud vrátí synchronní hodnotu, bude zabalena do Promise.resolve().
await – operátor použitelný pouze uvnitř async funkce. Pozastaví vykonávání async funkce dokud se Promise nevyřeší, ale neblokuje call stack – ostatní kód může běžet dál.
async function loadData() {
try {
const response = await fetch('/api/data') // pozastaví loadData()
const data = await response.json()
return data // zabaleno do Promise.resolve(data)
} catch (e) {
console.error('Chyba:', e)
// await na rejected Promise hodí výjimku → zachytíme try/catch
}
}
// Ekvivalent generátoru z předchozí sekce!
Pokud await dostane odmítnutý Promise, hodí výjimku (throw). Bez try/catch bude chyba unhandled rejection. Alternativa: .catch() na vráceném Promise.
Paralelní vs. sekvenční await
// ❌ SEKVENČNÍ – každý čeká na předchozí (pomalé!)
const a = await fetchA()
const b = await fetchB()
// ✅ PARALELNÍ – oba požadavky spuštěny najednou
const [a, b] = await Promise.all([fetchA(), fetchB()])
Top-level await (ES2022)
V modulech (type="module") lze používat await na nejvyšší úrovni bez obalení do async funkce:
// module.js
const data = await fetch('/api/config').then(r => r.json())
🛑 AbortController & AbortSignal
AbortController – API pro zrušení (abort) asynchronních operací. Obsahuje metodu abort() a vlastnost signal (AbortSignal), která se předává do operace (fetch, addEventListener...).
const controller = new AbortController()
const btn = document.querySelector('#cancel')
btn.addEventListener('click', () => controller.abort())
try {
const res = await fetch('/api/slow', { signal: controller.signal })
const data = await res.json()
console.log('ok', data)
} catch (e) {
if (e.name === 'AbortError') console.log('Požadavek zrušen')
else throw e // jiná chyba – znovu hodit
}
AbortSignal lze propojit i s jinými API: addEventListener('event', handler, { signal }) – při abortu se listener automaticky odstraní.
🌊 Asynchronní iterátory a for await...of
[Symbol.asyncIterator], jehož next() metoda vrací Promise s { value, done }.
async function* asyncNumbers() {
yield 1
await new Promise(r => setTimeout(r, 100))
yield 2
yield 3
}
;(async () => {
for await (const num of asyncNumbers()) {
console.log(num) // 1, pak 2, pak 3
// break → zavře iterátor (spustí return())
}
})()
Praktické využití: čtení streamů (ReadableStream), postupné zpracování výsledků z databáze, Server-Sent Events.
async function* (asynchronní generátor)
Kombinace async a function* – může používat jak await, tak yield. Vhodné pro produkci sekvencí hodnot, kde každá může být asynchronně získána.
⏱️ Debounce a Throttle
// Debounce – volej až po pauze
const debounce = (fn, wait = 200) => {
let timer
return (...args) => {
clearTimeout(timer) // zruš předchozí timer
timer = setTimeout(() => fn(...args), wait) // naplánuj nový
}
}
window.addEventListener('input', debounce(e => search(e.target.value), 250))
// Throttle – volej maximálně jednou za interval
const throttle = (fn, wait = 100) => {
let locked = false
return (...args) => {
if (locked) return // blokováno – ignoruj
locked = true
fn(...args) // okamžité volání
setTimeout(() => { locked = false }, wait) // uvolni po wait ms
}
}
window.addEventListener('scroll', throttle(() => updateUI(), 100))
| Vzor | Kdy se funkce zavolá | Typické použití |
|---|---|---|
| Debounce | Po poslední události + čekací doba | Search input, form validation |
| Throttle | Maximálně 1× za interval | Scroll, resize, mousemove |
📋 Shrnutí okruhu 2
- JS je single-threaded, asynchronicita není multithreading – deleguje na runtime, výsledky přijímá přes event loop.
- Event loop: po vyprázdnění call stacku nejprve zpracuje všechny microtasky, pak jeden macrotask.
- Promise má tři neměnné stavy (pending, fulfilled, rejected);
.then()vždy vrátí nový Promise umožňující chaining. - async/await je syntaktický cukr nad generátory a Promises –
awaitpozastaví async funkci, ne celý JS engine. - Generátory (
function*,yield) umožňují „líné" zpracování sekvencí a jsou základem konceptu async/await.
Kontrolní otázky a odpovědi
-
Co je event loop a jak funguje?
Event loop je mechanismus, který hlídá call stack a fronty úloh. Pokud je call stack prázdný: (1) zpracuje všechny microtasky (Promise.then, queueMicrotask), (2) vezme jeden macrotask (setTimeout callback, event handler) a vloží ho na call stack. Díky tomu JS může být single-threaded, ale zároveň „asynchronní".
-
Jaký je rozdíl mezi micro queue a macro queue?
Microtask queue má vyšší prioritu – po každém macrotasku (a po synchronním kódu) se zpracují všechny microtasky, včetně nově přidaných. Micro: Promise.then, queueMicrotask, MutationObserver. Macro: setTimeout, setInterval, DOM events, I/O.
-
Co je Promise a jaké má stavy?
Promise je objekt zapouzdřující výsledek asynchronní operace. Stavy:
pending(čeká),fulfilled(splněn),rejected(odmítnut). Stav je jednosměrný – nelze se vrátit do pending..then()vždy vrátí nový Promise, což umožňuje chaining. -
Co jsou generátory a jak se liší od async/await?
Generátor (
function*) je funkce, která může být pozastavena (yield) a obnovena (next()). async/await je syntaktický cukr nad generátory –awaitodpovídáyielda runtime automaticky obnoví provádění po splnění Promise. Generátory jsou obecnější – lze je využít i pro synchronní „líné" sekvence. -
Jak se liší debounce od throttle?
Debounce odloží volání o danou dobu po poslední události (timer se resetuje při každé nové události). Throttle zavolá funkci maximálně jednou za daný interval. Debounce: vyhledávací input. Throttle: scroll handler.
-
K čemu slouží AbortController?
AbortController umožňuje zrušit probíhající asynchronní operace (fetch, event listenery). Vytvoří se controller, jeho
signalse předá do operace, a volánímcontroller.abort()se operace přeruší – fetch hodíAbortError. -
Co je callback hell a jak ho řeší Promises / async-await?
Callback hell je antipattern hlubokého vnoření asynchronních callbacků, který způsobuje nečitelnost a obtížné ošetření chyb. Promises řeší chaining (
.then().then().catch()) – lineární tok. async/await jde ještě dál – asynchronní kód vypadá jako synchronní, chyby lze zachytávat try/catch.