En hær af funktioner
Den følgende kode opretter en array af shooters.
Hver funktion er ment at vise sit nummer. Men noget er forkert…
function makeArmy() {
let shooters = [];
let i = 0;
while (i < 10) {
let shooter = function() { // opret en shooter funktion,
alert( i ); // der skal vise sit nummer
};
shooters.push(shooter); // og tilføj den til arrayet
i++;
}
// ...og returner arrayet af shooters
return shooters;
}
let army = makeArmy();
// alle shooters viser 10 i stedet for deres tal 0, 1, 2, 3...
army[0](); // 10 fra shooter nummer 0
army[1](); // 10 fra shooter nummer 1
army[2](); // 10 ...og så videre.
Hvorfor får alle shooters samme værdi?
Fiks koden så koden virker som forventet.
Lad os undersøge hvad der faktisk sker inde i makeArmy. Måske står løsningen så klarere.
-
Den opretter et tomt array
shooters:let shooters = []; -
Fylder den med funktioner via
shooters.push(function)i løkken.Hvert element er en funktion, så det resulterer i et array der ser således ud:
shooters = [ 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); } ]; -
Arrayet returneres fra funktionen.
Senere vil et kald til et element i arrayet, f.eks.
army[5]()vil hente elementetarmy[5]fra arrayet (som er en funktion) og kalde den.Men, hvorfor viser alle sådanne funktioner så samme værdi,
10?Det skyldes, at der ikke er en lokal variabel
iinde ishooter-funktionerne. Når en sådan funktion kaldes, tager denifra dens ydre leksikale miljø. Så hvad er værdien afinår funktionen kaldes?Kig på koden:
function makeArmy() { ... let i = 0; while (i < 10) { let shooter = function() { // shooter funktion alert( i ); // skal vise sit nummer }; shooters.push(shooter); // tilføj funktionen til arrayet i++; } ... }Vi kan se at alle
shooterfunktioner er oprettet i det leksikale miljø afmakeArmy()funktionen. Men nårarmy[5]()kaldes, harmakeArmyallerede afsluttet sin job, og den endelige værdi afier10(whilestopper vedi=10).Som resultat får alle
shooterfunktioner samme værdi fra det ydre leksikale miljø og det er den sidste værdi,i=10.Som du kan se ovenfor så oprettes der et nyt leksikalt miljø ved hver iteration af
while {...}blokken. Så for at fikse dette, kan vi kopiere værdien afitil en variabel inden iwhile {...}blokken, som dette:function makeArmy() { let shooters = []; let i = 0; while (i < 10) { let j = i; let shooter = function() { // shooter funktion alert( j ); // skal vise sit nummer }; shooters.push(shooter); i++; } return shooters; } let army = makeArmy(); // Nu virker koden som den skal army[0](); // 0 army[5](); // 5Her vil
let j = ideklarere en lokal variabeljog kopiereiover i den. Primitiver kopieres “ved deres værdi”, så vi får en reelt uafhængig kopi afi, der tilhører den aktuelle løkkes iteration.Shooters virker korrekt nu fordi værdien af
inu lever et lidt tættere på. Ikke imakeArmy()Lexical Environment, men i det Lexical Environment der svarer til den aktuelle løkkes iteration:Dette problem kunne undgås hvis vi brugte
fori stedet forwhile, som dette:function makeArmy() { let shooters = []; for(let i = 0; i < 10; i++) { let shooter = function() { // shooter funktion alert( i ); // bør vise sit nummer }; shooters.push(shooter); } return shooters; } let army = makeArmy(); army[0](); // 0 army[5](); // 5Det er grundlæggende det samme fordi
forgennem hver iteration genererer en ny leksikale miljø med sin egen variabeli. Såshootergenereret i hver iteration refererer til dens egeni, fra den pågældende iteration.
Nu, efter du har lagt så meget energi i at læse dette, og den endelige opskrift er så enkel – bare brug for, kan du måske tænke – var det værd det?
Vel, hvis du kunne svare spørgsmålet nemt, ville du ikke have læst løsningen. Så håber jeg, at denne opgave har hjulpet dig med at forstå tingene lidt bedre.
Desuden er der faktisk tilfælde hvor man hellere foretrækker while frem for for, og andre scenarier hvor sådanne problemer opstår.