Armáda funkcÃ
NásledujÃcà kód vytvoÅà pole stÅelci.
Každá funkce má vypsat své ÄÃslo. Ale nÄco je Å¡patnÄâ¦
function vytvoÅArmádu() {
let stÅelci = [];
let i = 0;
while (i < 10) {
let stÅelec = function() { // vytvoÅÃme funkci stÅelec,
alert( i ); // která by mÄla zobrazit své ÄÃslo
};
stÅelci.push(stÅelec); // a pÅidáme ji do pole
i++;
}
// ...a vrátÃme pole stÅelci
return stÅelci;
}
let armáda = vytvoÅArmádu();
// vÅ¡ichni stÅelci zobrazà 10 mÃsto svých ÄÃsel 0, 1, 2, 3...
armáda[0](); // 10 od stÅelce ÄÃslo 0
armáda[1](); // 10 od stÅelce ÄÃslo 1
armáda[2](); // 10 ...a tak dále.
ProÄ vÅ¡ichni stÅelci zobrazujà stejnou hodnotu?
Opravte kód, aby fungoval tak, jak je zamýšleno.
Prozkoumejme, co pÅesnÄ se dÄje uvnitÅ funkce vytvoÅArmádu, a ÅeÅ¡enà bude zÅejmé.
-
VytvoÅà se prázdné pole
stÅelci:let stÅelci = []; -
V cyklu se naplnà funkcemi pomocÃ
stÅelci.push(function).Každý prvek je funkce, takže výsledné pole vypadá takto:
stÅelci = [ function () { alert(i); }, function () { alert(i); }, function () { alert(i); }, function () { alert(i); }, function () { alert(i); }, function () { alert(i); }, function () { alert(i); }, function () { alert(i); }, function () { alert(i); }, function () { alert(i); } ]; -
Toto pole funkce vrátÃ.
Pak pozdÄji volánà kteréhokoli jeho prvku, napÅ.
armáda[5](), naÄte z pole prvekarmáda[5](což je funkce) a zavolá jej.ProÄ nynà vÅ¡echny tyto funkce zobrazà stejnou hodnotu,
10?Je to proto, že uvnitÅ funkcÃ
stÅelecneexistuje žádná lokálnà promÄnnái. Když je taková funkce volána, pÅevezmeiz vnÄjÅ¡Ãho lexikálnÃho prostÅedÃ.Jaká pak bude hodnota promÄnné
i?Když se podÃváme na zdrojový kód:
function vytvoÅArmádu() { ... let i = 0; while (i < 10) { let stÅelec = function() { // funkce stÅelec alert( i ); // by mÄla zobrazit své ÄÃslo }; stÅelci.push(stÅelec); // pÅidá funkci do pole i++; } ... }VidÃme, že vÅ¡echny funkce
stÅelecjsou vytvoÅeny v lexikálnÃm prostÅedà funkcevytvoÅArmádu(). Když je vÅ¡ak volánaarmáda[5](), funkcevytvoÅArmádujiž ukonÄila svou práci a poslednà hodnotaije10(whilese zastavà nai=10).Výsledkem je, že vÅ¡echny funkce
stÅelecpÅevezmou z vnÄjÅ¡Ãho lexikálnÃho prostÅedà stejnou hodnotu a tou bude poslednà hodnota,i=10.Jak vidÃte výše, pÅi každé iteraci bloku
while {...}bude vytvoÅeno nové lexikálnà prostÅedÃ. Abychom to opravili, můžeme zkopÃrovat hodnotuido promÄnné uvnitÅ blokuwhile {...}tÅeba takto:function vytvoÅArmádu() { let stÅelci = []; let i = 0; while (i < 10) { let j = i; let stÅelec = function() { // funkce stÅelec alert( j ); // by mÄla zobrazit své ÄÃslo }; stÅelci.push(stÅelec); i++; } return stÅelci; } let armáda = vytvoÅArmádu(); // Nynà kód funguje správnÄ armáda[0](); // 0 armáda[5](); // 5Zde
let j = ideklaruje âiteraÄnÄ lokálnÃâ promÄnnouja zkopÃruje do nÃi. Primitivy se kopÃrujà âhodnotouâ, takže ve skuteÄnosti zÃskáme nezávislou kopiii, která patÅà do aktuálnà iterace cyklu.StÅelci budou fungovat správnÄ, protože hodnota
inynà existuje trochu blÞe. Nenà v lexikálnÃm prostÅedà funkcevytvoÅArmádu(), ale v lexikálnÃm prostÅedÃ, které odpovÃdá aktuálnà iteraci cyklu:Tomuto problému se lze vyhnout i tak, že na zaÄátku použijeme
for, tÅeba takto:function vytvoÅArmádu() { let stÅelci = []; for(let i = 0; i < 10; i++) { let stÅelec = function() { // funkce stÅelec alert( i ); // by mÄla zobrazit své ÄÃslo }; stÅelci.push(stÅelec); } return stÅelci; } let armáda = vytvoÅArmádu(); armáda[0](); // 0 armáda[5](); // 5To je v zásadÄ totéž, protože
forpÅi každé své iteraci vygeneruje nové lexikálnà prostÅedà se svou vlastnà promÄnnoui. TakžestÅelecgenerovaný v každé iteraci odkazuje na své vlastnÃipÅÃmo z této iterace.
Když jste vložili tolik námahy do pÅeÄtenà tohoto ÅeÅ¡enà a koneÄný recept je tak jednoduchý â prostÄ použijeme for, můžete se divit â mÄlo to cenu?
Inu, kdybyste na tuto otázku dokázali snadno odpovÄdÄt, neÄetli byste ÅeÅ¡enÃ. Snad vám tedy tato úloha pomohla trochu lépe vÅ¡emu porozumÄt.
KromÄ toho zajisté existujà pÅÃpady, kdy ÄlovÄk dává pÅednost while pÅed for, i jiné scénáÅe, v nichž takové problémy opravdu nastanou.