Domů O mně Služby Portfolio Učím Blog Kontakt
← Zpět na učím

03.09 Lexikální Scope

Výuka

Proč lexikální scope?

V lekci 08 jsi viděl, že scope určuje, kde je proměnná viditelná. Ale jak JavaScript rozhoduje, která proměnná patří do kterého scope?

const color = "modrá";

function outer() {
  const color = "červená";

  function inner() {
    console.log(color);  // Která "color"? Modrá nebo červená?
  }

  inner();
}

outer();  // → "červená"

Lexikální scope (lexical scope) říká: Kde kód píšeš, tam patří. Ne kde se volá, ne kdy běží - ale kde je napsaný.

Proč je to užitečné?

  • Předvídatelnost - stačí se podívat na kód, nemusíš sledovat, odkud se volá
  • Čitelnost - vidíš hned, jaké proměnné funkce používá
  • Bezpečnost - funkce nemůže "náhodou" přistoupit k proměnným, které by neměla

Představ si to jako: Krabice v krabicích. Vnitřní krabice vidí obsah vnější, ale vnější krabice nevidí dovnitř té menší. Kde krabici umístíš (napíšeš v kódu), tam patří.

Jak to funguje?

Lexikální scope = scope určený tím, KDE kód píšeš:

1. JavaScript při kompilaci "vidí", kde jsou funkce a bloky napsané
2. Vytvoří "scope bubbles" (bubliny scope) - vnořené oblasti
3. Každá bublina vidí proměnné ve svém scope + všech vnějších
4. Při spuštění hledá proměnné ZEVNITŘ VEN (nikdy dovnitř!)

Důležité:

  • Scope se určuje při psaní kódu (author-time), ne při spuštění (run-time)
  • Nemůžeš změnit scope za běhu programu (až na eval a with, které NIKDY nepoužívej)
  • Hledání probíhá jen jedním směrem - zevnitř ven

Klíčové koncepty

  • Lexikální = podle místa v kódu - kde funkci/proměnnou napíšeš, tam patří
  • Nested scope (vnořené) - scope uvnitř scope, jako krabice v krabici
  • Lookup (hledání) - JavaScript hledá proměnnou od nejbližšího scope ven
  • Shadowing - vnitřní proměnná "zakryje" vnější se stejným jménem
  • One-way street - můžeš hledat jen ven, nikdy dovnitř

JavaScript

Příklad 1: Scope určený místem v kódu

const name = "Globální";

function showName() {
  console.log(name);  // Kde se podívá? Místně → Ven (globální)
}

showName();
// → "Globální"

function outer() {
  const name = "Vnější funkce";

  function inner() {
    console.log(name);  // Kde se podívá? Místně → Ven (outer) ✅
  }

  inner();
}

outer();
// → "Vnější funkce"

Co se stalo?

  • Funkce showName nevidí proměnnou místně, tak hledá venku → najde globální name
  • Funkce inner nevidí proměnnou místně, tak hledá venku → najde name z outer
  • Lexikální scope = kde je funkce napsaná (uvnitř outer), ne odkud se volá!

Příklad 2: Lookup - hledání zevnitř ven

const a = "úroveň 1";

function level2() {
  const b = "úroveň 2";

  function level3() {
    const c = "úroveň 3";

    console.log(c);  // ✅ Najde v úrovni 3
    console.log(b);  // ✅ Hledá ven → úroveň 2
    console.log(a);  // ✅ Hledá ven → úroveň 2 (nemá) → úroveň 1 ✅
  }

  level3();
  console.log(c);  // ❌ Error: c is not defined
}

level2();

Co se stalo?

  • JavaScript hledá proměnnou nejdřív lokálně
  • Pokud nenajde, hledá v dalším vnějším scope
  • Pokračuje až do globálního scope
  • Pokud ani tam nenajde, vyhodí chybu ReferenceError
  • Nikdy nehledá dovnitř - level2 nemůže vidět c z level3!

Příklad 3: Shadowing - překrytí proměnné

const value = "vnější";

function test() {
  const value = "vnitřní";  // Zakryje vnější value

  console.log(value);  // Která value? Místní! ✅
}

test();
// → "vnitřní"

console.log(value);
// → "vnější" (globální nezměněna)

Co se stalo?

  • JavaScript hledá value nejdřív v test funkci → najde! ✅
  • Přestane hledat - našel nejbližší match
  • Vnější value stále existuje, ale je zakrytá (shadowed)
  • Obě proměnné existují zároveň, jen vnitřní má přednost

Příklad 4: Scope se určuje při psaní, ne volání

const color = "červená";

function showColor() {
  console.log(color);  // Kde hledá? Podle místa DEFINICE!
}

function test() {
  const color = "modrá";
  showColor();  // Zavolá showColor ZDE
}

test();
// → "červená" (ne "modrá"!)

Co se stalo?

  • Funkce showColor je definovaná v globálním scope
  • Proto hledá color v globálním scope (kde je napsaná)
  • Nezáleží, že ji voláme z test (kde je color = "modrá")
  • Lexikální scope = určeno místem definice, ne volání!

Příklad 5: Lookup zastaví u první shody

const x = "globální";

function outer() {
  const x = "outer";

  function middle() {
    const x = "middle";

    function inner() {
      console.log(x);  // Najde v middle a přestane hledat
    }

    inner();
  }

  middle();
}

outer();
// → "middle"

Co se stalo?

  • JavaScript hledá x v inner → nenajde
  • Hledá v middlenajde!
  • Přestane hledat - našel první shodu
  • Nikdy se nedostane k x z outer nebo globální

Příklad 6: Vnořené funkce vidí "ven"

function makeCounter() {
  let count = 0;  // Lokální proměnná v makeCounter

  return function() {
    count++;      // Vidí count z vnějšího scope!
    return count;
  };
}

const counter = makeCounter();
console.log(counter());  // → 1
console.log(counter());  // → 2
console.log(counter());  // → 3

Co se stalo?

  • Vnitřní funkce je definovaná uvnitř makeCounter
  • Díky lexikálnímu scope vidí proměnnou count z vnějšího scope
  • I když makeCounter už skončila, vnitřní funkce si pamatuje count
  • To je closure - o tom více v lekci 14!

TypeScript

TypeScript respektuje stejná pravidla lexikálního scope jako JavaScript. Scope funguje identicky!

const message: string = "Globální";

function outer(): void {
  const message: string = "Outer";

  function inner(): void {
    console.log(message);  // Hledá podle lexikálního scope
  }

  inner();  // → "Outer"
}

outer();

TypeScript kontroluje scope při kompilaci:

const x: number = 10;

function test(): void {
  console.log(x);  // ✅ TypeScript ví, že x existuje venku
  console.log(y);  // ❌ Compile error: Cannot find name 'y'
}

test();

TypeScript přidává:

  • Kontrola existence proměnných - varování, pokud proměnná není v žádném dostupném scope
  • Lepší IntelliSense - editor ti ukáže, jaké proměnné jsou v daném scope dostupné
  • Typová bezpečnost - kontroluje typy napříč všemi scope

Rozdíl JS vs TS

JavaScript:

const name = "Alice";

function test() {
  console.log(name);  // ✅ Funguje
  console.log(age);   // 💥 Runtime error: age is not defined
}

test();

TypeScript:

const name: string = "Alice";

function test(): void {
  console.log(name);  // ✅ Funguje
  console.log(age);   // ❌ Compile error: Cannot find name 'age'
}

test();

Rozdíl:

  • JavaScript nenajde chybu, dokud kód nespustíš
  • TypeScript najde chybu hned při psaní - editor tě upozorní!
  • Obojí používá stejný lexikální scope, ale TS ho kontroluje dřív

Tip

💡 Scope se určuje při psaní, ne volání:

// ❌ Mylná představa - "showColor uvidí color z caller"
const color = "červená";

function showColor() {
  console.log(color);
}

function test() {
  const color = "modrá";
  showColor();  // Myslíš, že vypíše "modrá"?
}

test();  // → "červená" (hledá podle místa DEFINICE!)

// ✅ Správná představa - scope podle místa definice
function test() {
  const color = "modrá";

  function showColor() {
    console.log(color);  // Definovaná UVNITŘ test → vidí color z test
  }

  showColor();  // → "modrá" ✅
}

test();

💡 Shadowing může být matoucí - vyhni se, pokud není nutný:

// ⚠️ Matoucí - stejný název na více místech
const name = "globální";

function test() {
  const name = "test";  // Zakryje globální

  function inner() {
    const name = "inner";  // Zakryje test
    console.log(name);
  }

  inner();
}

// ✅ Lepší - jasné názvy
const globalName = "globální";

function test() {
  const testName = "test";

  function inner() {
    const innerName = "inner";
    console.log(innerName);
  }

  inner();
}

💡 Vnitřní scope nemůže "uniknout" ven:

function outer() {
  if (true) {
    const secret = "Tajemství";
  }

  console.log(secret);  // ❌ Error: secret is not defined
  // secret existuje JEN uvnitř if bloku!
}

// ✅ Pokud potřebuješ proměnnou venku, deklaruj ji venku
function outer() {
  let secret;  // Deklarace ve vnějším scope

  if (true) {
    secret = "Tajemství";  // Přiřazení
  }

  console.log(secret);  // ✅ Funguje
}

Kvíz

Co je lexikální scope?

const x = "globální";

function outer() {
  const x = "outer";

  function inner() {
    console.log(x);
  }

  return inner;
}

const fn = outer();
fn();  // Co vypíše?

- Lexikální scope se určuje místem definice, ne volání

- Funkce inner je definovaná uvnitř outer, proto vidí x z outer

- Díky closure si funkce pamatuje scope, i když outer skončila

- x je dostupná díky lexikálnímu scope a closure

🎯 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 →

Připraveni začít?

Zaregistrujte se a získejte přístup ke všem dílům tohoto seriálu

Kontaktujte mě

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


Struktura lekcí (souborový strom)

06. Typescript specifika
  • v přípravě
08. Moduly tridy
  • v přípravě
09. React zaklady
  • v přípravě
10. React hooks
  • v přípravě
12. Nextjs server
  • v přípravě
13. Databaze auth
  • v přípravě
14. Nextjs pokrocile
  • v přípravě