Javascript a oblast působnosti proměnných – díl první

V sérii článků Petra Staníčka se podíváme na jednu z oblastí JavaScriptu, která většině programátorů může připadat samozřejmá a nepřekvapivá, totiž na oblast působnosti proměnných. V prvním článku budou na pořadu dne obyčejné proměnné a funkce. Myslíte si, že máte v problematice jejich působnosti zcela jasno?

Seriál: Javascript a oblast působnosti proměnných (3 díly)

  1. Javascript a oblast působnosti proměnných – díl první 7.8.2009
  2. Javascript a oblast působnosti proměnných – díl druhý 12.8.2009
  3. Javascript a oblast působnosti proměnných – díl třetí 17.8.2009

Poznámka redakce: Petr Staníček původně napsal jeden velmi dlouhý článek, který by byl na jedno „učtení“ příliš náročný. Proto jsme po vzájemné dohodě sáhli k jeho rozdělení. První část se věnuje věcem, které by měly být programátorům v JS důvěrně známé – totiž obory platností proměnných a funkcí. V druhém díle bude následovat popis objektů, tříd, metod a možností zapouzdření, ve třetím díle pak uzávěry (closures). První díl je tedy jakousi „předehrou“, a proto může připadat zkušenějším jako opakování známých věcí. 

Napsat něco o oblasti působnosti proměnných v Javascriptu jsem se rozhodl hlavně proto, že jsem nedávno v jedné z velmi temných chodbiček této problematiky zabředl až po krk – a nikoli poprvé – a potřebuji si v tom konečně sám udělat jednou provždy trochu jasno. Přesně podle hesla: „Jestli v něčem nemáš jasno, napiš o tom článek. Jestliže to opravdu nechápeš, napiš o tom knihu. A pokud pořád ještě tápeš, začni to učit.“ Ona ta platnost proměnných v Javascriptu je známý problém zavalený spoustou dezinformací, mylných představ i fám a prakticky každý javascriptový programátor na to dříve či později narazil. A tvrdí-li někdo, že s tím nikdy problém neměl, má ho nejspíš dodnes.

Globální a lokální proměnné

Rozlišování mezi lokální a globální platností proměnných patří mezi základní programátorské dovednosti; míra a způsob jejich odlišení zase mezi základní parametry každého programovacího jazyka. Většina jazyků má v tomto ohledu velmi striktní pravidla, u Javascriptu se to může zdát malinko divočejší, ale pravidla jsou tu také. Ovšem v některých případech poněkud méně průhledná.

Obecně lze říci, že každá proměnná má platnost pro tu úroveň kódu, v níž byla deklarována – aneb jak se říká, byla „uvařena“ (kvůli klíčovému slovu var, které se k deklaraci proměnných používá). Pokud je proměnná deklarována na nejvyšší úrovni, tedy přímo v dokumentu, mimo tělo nějaké funkce, stává se tímto okamžikem proměnnou globální a je dostupná z kteréhokoli místa kódu.

var x = 1;function test() {
   x++;
   alert(x);
   }
x = 2;
test();

Globální proměnné x je deklarací přiřazena hodnota 1. Následně na nejvyšší úrovni kódu je jí přizena hodnota 2. Poté je zavolána funkce test(), která její hodnotu zvýší o jedna a vypíše její aktuální hodnotu, tedy 3.

Lokální proměnné

Pokud je proměnná deklarována uvnitř nějaké funkce, její platnost se zužuje pouze na kód uvnitř této funkce. Navíc je zde jeden zásadní rozdíl: tato proměnná nevzniká hned, ale až při volání své „mateřské“ funkce. A voláme-li ji vícekrát, vzniká pokaždé znova.

function test() {
   var x = 1;
   alert(x);
   x++;
   }
test();
test();

Při načítání skriptu vznikne funkce test, ovšem nic z toho, co definuje její kód – tedy ani proměnná x  – ještě neexistuje. Teprve při volání test() vznikne (lokální) proměnná x, dostane hodnotu 1, ta se vypíše, její hodnota se zvýší o 1 a funkce skončí. Tím tato proměnná x zaniká. Dalším voláním test() se vytvoří nový „klon“ proměnné x a stane se totéž – tj. vypíše se 1, hodnota x se zvýší o jedna, načež zanikne.

Lokální proměnná se uvnitř své „mateřské“ funkce chová jako globální, je tedy dostupná celé této úrovni kódu, tedy i všem funkcím deklarovaným uvnitř uzavírající funkce.

function test() {
   function plus() {
      x++;
      }
   var x = 1;
   plus();
   alert(x);
   }
test();

Zavolá se funkce test(), ta deklaruje svou lokální funkci plus, poté deklaruje svou lokální proměnnou x s hodnotou 1, zavolá funkci plus pro kterou je x proměnnou globální, zvýší tedy její hodnotu o jedna a výsledná hodnota 2 se vypíše.

Je důležité vědět, že mezi globálními a lokálními proměnými stejného jména není vůbec žádná vazba a mohou tedy existovat dvě různé proměnné se stejným jménem, ale odlišným oborem působnosti.

function test() {
   var x = 1;
   alert(x);
   }
var x = 10;
test();
alert(x);

Zde se nejprve deklaruje funkce test, poté globální proměnná x s hodnotou 10. Poté se volá funkce test(), která si vytvoří svou lokální proměnnou x s hodnotou 1, a tu zobrazí. Načež se zobrazí hodnota jiné, tentokrát globální proměnné x, tedy 10.

Vše je lepší uvařené

Jedním z největších zel Javasciptu je skutečnost, že explicitní deklarování proměnných není povinné. Nicméně jeho nepoužívání může vést k řadě problémů a skrytých chyb a je dobrým zvykem slušného programátora vařit úplně každou proměnnou – především právě proto, aby měl zcela jasno v mezích její platnosti.

Pokud se tak z jakéhokoli důvodu nestane, je aspoň důležité vědět, že neznámé (nedeklarované) proměnné se při prvním použití automaticky deklarují na globální úrovni.

function test() {
   x = 10;
   alert(x);
   }
test();
x++;
alert(x);

Zde se volá funkce test(), která přiřazuje hodnotu 10 dosud nikde nedeklarované proměnné x, proto se automaticky vytvoří globální proměnná x a zobrazí se její hodnota 10. Poté globální část kódu této (již deklarované!) proměnné zvýší hodnotu o jedna a následně zobrazí hodnotu 11.

Je opravdu důležité si na tuto vlastnost Javascriptu dávat dobrý pozor, nezřídka se stává, že takováto nedeklarovaná proměnná je použita domněle jako lokální – třeba pro index pole nebo řídicí proměnnou cyklu – ale protože je ve skutečnosti proměnnou globální, její hodnotu někdo zvenčí neočekávaně změní a mohou se začít dít jen těžko odhalitelné chyby. Deklarování proměnných by si měl každý dát za pravidlo i v těch nejmenších a nejpitomějších skriptících – a pokud píšeme nějakou knihovnu nebo vůbec jakkoli sdílený kód, který se může ocitnout ve stránce společně s kódy, které nemáme plně pod svou kontrolou, je to zcela bez diskuse a mělo by to být naprosto povinné.

Proměnné a funkce

Dosud jsem mluvil jen o proměnných, nicméně z hlediska Javascriptu se jako proměnná chová i každá funkce a platí pro ně totéž, co bylo řečeno o proměnných výše. Obvyklý zápis funkce je v podstatě zkrácený zápisem deklarace proměnné a přiřazením funkce jako její hodnoty – neboli tento tvar:

function test(a) {
   return a + 1;
   }

je ekvivalentní tomuto zápisu:

var test = function test(a) {
   return a + 1;
   }

A tak stejně jako proměnné, i funkce definované na globální úrovni jsou globální a dostupné v celém kódu, funkce definované v těle jiné funkce jsou lokální, dostupné jen pro její kód a z hlediska vnějšího kódu neexistují.

function test() {
   function plus(x) { return x + 1 }
   alert( plus(1) );
   }
test();
alert( plus(1) );

Zde se zavolala funkce test, která volá svou interní funkci plus, která zvýší předaný parametr o jedničku. Poté se úplně stejně volá funkce plus z globální úrovně, ale tam již taková funkce neexistuje, což vyvolá chybu Javascriptu.

V dalším díle se podíváme na problematiku tříd a metod, vlastnosti objektů a zapouzdření.

Autor je návrhář UI/UX, analytik, grafik, javascriptový vývojář a advocatus diaboli ex offo.

Věděli jste, že nám můžete zasílat zprávičky? (Jen pro přihlášené.)

Komentáře: 48

Přehled komentářů

Dero funkce
Aleš Roubíček Re: funkce
karf Re: funkce
ah01 Re: funkce
Petr Staníček Re: funkce
karf Re: funkce
Petr Staníček Re: funkce
karf Re: funkce
Petr Staníček Re: funkce
Dero Re: funkce
Dero Re: funkce
blizz.boz Re: Javascript a oblast působnosti proměnných - díl první
Aleš Roubíček Re: Javascript a oblast působnosti proměnných - díl první
blizz.boz Re: Javascript a oblast působnosti proměnných - díl první
Aichi Re: Javascript a oblast působnosti proměnných - díl první
blizz.boz Re: Javascript a oblast působnosti proměnných - díl první
Aichi Re: Javascript a oblast působnosti proměnných - díl první
mykhal Re: Javascript a oblast působnosti proměnných - díl první
Petr Staníček Re: Javascript a oblast působnosti proměnných - díl první
v6ak Poslední příklad: zvyšuje o jedničku?
Petr Staníček Re: Poslední příklad: zvyšuje o jedničku?
Petr Staníček Re: Poslední příklad: zvyšuje o jedničku?
v6ak Re: Poslední příklad: zvyšuje o jedničku?
Petr Staníček Re: Poslední příklad: zvyšuje o jedničku?
v6ak Zacyklení při rekurzi
Michal Augustýn Re: Javascript a oblast působnosti proměnných - díl první
Daniel Steigerwald Re: Javascript a oblast působnosti proměnných - díl první
Petr Staníček Re: Javascript a oblast působnosti proměnných - díl první
Daniel Steigerwald Re: Javascript a oblast působnosti proměnných - díl první
KLoK Re: Javascript a oblast působnosti proměnných - díl první
Leoš Ondra Uvaření globálních a IE
v6ak Hack s anonymkou
Daniel Steigerwald Re: Hack s anonymkou
v6ak Re: Hack s anonymkou
Daniel Steigerwald Re: Hack s anonymkou
v6ak Re: Hack s anonymkou
Martin Malý Re: Hack s anonymkou
Daniel Steigerwald Re: Hack s anonymkou
v6ak Re: Hack s anonymkou
Daniel Steigerwald JavaScript díl 1 - proměnné
Martin Malý Re: JavaScript díl 1 - proměnné
Timy Re: JavaScript díl 1 - proměnné
Borek Bernard Re: JavaScript díl 1 - proměnné
Daniel Steigerwald Re: JavaScript díl 1 - proměnné
povinná Re: JavaScript díl 1 - proměnné
PetrP Re: Javascript a oblast působnosti proměnných - díl první
Johny_S předání proměnné
v6ak Re: předání proměnné
Zdroj: https://www.zdrojak.cz/?p=3060