Samozrejme, ze je. Kazda funkcia vytvara scope, a jednotlive scopy (ako sa to povie po slovensky? Rozsahy platnosti?) su do seba vnorene presne tak, ako vnaras funkcie. Cize majme nieco taketo:
Kód:
function e() {
var p = 3.14;
function f() {
var n = 42;
//miesto X
}
function g() {
}
// miesto Y
}
function h() {
}
A taky ten strom pre scopy vyzera nejak takto
Kód:
global
|__ e()
| |__ p = 3.14
| |__ f()
| | |__ n = 42
| | |__ [X]
| |__ g()
| |__ [Y]
|__ h()
No a pravidlo je, ze ked si raz na nejakom mieste v tom strome, tak "vidis" vsetko, co je na tvojej vetve (to su tie vertikalne ciary) a plus vsetko na vetvach v hierarchii vyssie, cize po tom strome mozes chodit iba hore-dole a dolava. To znamena, ze ak som v mieste X (oznacil som ho aj v kode, aj v tom strome), mam pristup k premennym n, p, k funkciam f (vidis? Som vnutri funkcie a zaroven k jej objektu mam pristup, samozrejme), g, e aj h. Ale ked som v mieste Y, nemozem uz siahat na premennu n, pretoze by som musel vojst do scopu f-ka.
V Javascripte je taka skvela ficura, ze jednotlive scopy si pamataju, ako ten strom z ich pohladu vyzeral, pricom v skutocnosti uz nemusi existovat vetva niekde vyssie. Majme ten kod, ktory som pisal v minulom prispevku, s tym alertovanim n-ka, len ho prepisem, nech su tie funkcie pomenovane.
Kód:
var n = 1;
(function F()
{
var n = 2;
setTimeout(function G()
{
alert(n);
}, 1);
})();
Strom teda vyzera takto:
Kód:
global
|__ n = 1
|__ F()
|__ n = 2
|__ G()
No a teraz, kedze setTimeout bezi asynchronne (co znamena tolko, ze nasledujuci kod necaka, kym sa ten setTimeout vykona), takze v case, ked G bezi, uz F vlastne nema preco existovat - vsetok kod v nej bol vykonany a keby som ju nepomenoval (tzn. ze by na nu neexistovala referencia), nema preco ani byt v pamati. A tu prave prichadza ta vyhoda, ze funkcia G si pamata, ze na svojej vetve mala to n rovne dvom. Kdezto keby miesto Gcka bol retazec, on si svoj scope (svoju vetvu) nevytvori a kedze bezi asynchronne, nema sa uz dovnutra vetvy Fka ako dostat a uvidi len to, co je na globalnej vetve, teda n rovno jednej.
Kde sa toto este vyuziva? Pozri na nasledovny kod:
Kód:
<ul>
<li>List item
<li>List item
<li>List item
</ul>
<script>
for (var c = document.getElementsByTagName('li'), e, i = 0; e = c[i]; i++)
{
e.onclick = function()
{
alert('Polozka cislo ' + i);
};
}
</script>
V skratke cyklom for to prejde vsetky <li> a kazdemu prideli takyto onclick. No ale problem je, ze keby si si skusil poklikat, vsade ti to vyhodi 3. To preto, ze i sa dostalo postupne na hodnotu 3 a dane onclick handlery vidia akurat to jedno i. Preto pozri, akym sposobom tam pridam closure (to je anonymna funkcia, ktora samozrejme vytvori scope):
Kód:
for (var c = document.getElementsByTagName('li'), e, i = 0; e = c[i]; i++)
{
(function()
{
var j = i;
e.onclick = function()
{
alert('Polozka cislo ' + j);
};
})();
}
Tentokrat kazdy jeden onclick handler lezi vo vlastnom scope, ktory vznikol vtedy, ked premenna i mala este hodnotu taku, ako by sme si predstavovali.