Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
The answer is: **Pete**.

A function gets outer variables as they are now, it uses the most recent values.
En funktion får ydre variabler som de er nu, den bruger de mest nyeste værdier.

Gamle variabelværdier gemmes ikke nogen steder. Når en funktion ønsker en variabel, tager den den nuværende værdi fra sit eget leksikale miljø eller det ydre.

Old variable values are not saved anywhere. When a function wants a variable, it takes the current value from its own Lexical Environment or the outer one.
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@ importance: 5

---

# Does a function pickup latest changes?
# Vil funktionen tage de seneste ændringer?

The function sayHi uses an external variable name. When the function runs, which value is it going to use?
Funktionen sayHi bruger en ekstern variabel name. Når funktionen kører, hvilken værdi vil den bruge?

```js
let name = "John";

function sayHi() {
alert("Hi, " + name);
alert("Hej, " + name);
}

name = "Pete";

sayHi(); // what will it show: "John" or "Pete"?
sayHi(); // Hvad vil den vise: "John" eller "Pete"?
```

Such situations are common both in browser and server-side development. A function may be scheduled to execute later than it is created, for instance after a user action or a network request.
Sådanne situationer er udbredte både i browser- og server-side udvikling. En funktion kan blive planlagt til at køre senere end den blev oprettet, for eksempel efter en brugerhandling eller en netværksforespørgsel.

So, the question is: does it pick up the latest changes?
Så spørgsmålet er: vil den tage de seneste ændringer?
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ function makeArmy() {
let shooters = [];

for(let i = 0; i < 10; i++) {
let shooter = function() { // shooter function
alert( i ); // should show its number
let shooter = function() { // shooter funktion
alert( i ); // bør vise sit nummer
};
shooters.push(shooter);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ function makeArmy() {

let i = 0;
while (i < 10) {
let shooter = function() { // shooter function
alert( i ); // should show its number
let shooter = function() { // shooter funktion
alert( i ); // bør vise sit nummer
};
shooters.push(shooter);
i++;
Expand All @@ -16,7 +16,6 @@ function makeArmy() {
/*
let army = makeArmy();

army[0](); // the shooter number 0 shows 10
army[5](); // and number 5 also outputs 10...
// ... all shooters show 10 instead of their 0, 1, 2, 3...
army[0](); // shooter nummer 0 viser 10
army[5](); // og nummer 5 viser også 10...
*/
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
describe("army", function() {
describe("army", function () {

let army;
before(function() {

before(function () {
army = makeArmy();
window.alert = sinon.stub(window, "alert");
});

it("army[0] shows 0", function() {
it("army[0] viser 0", function () {
army[0]();
assert(alert.calledWith(0));
});


it("army[5] shows 5", function() {
it("army[5] viser 5", function () {
army[5]();
assert(alert.calledWith(5));
});

after(function() {
after(function () {
window.alert.restore();
});

Expand Down
57 changes: 27 additions & 30 deletions 1-js/06-advanced-functions/03-closure/10-make-army/solution.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@

Let's examine what exactly happens inside `makeArmy`, and the solution will become obvious.
Lad os undersøge hvad der faktisk sker inde i `makeArmy`. Måske står løsningen så klarere.

1. It creates an empty array `shooters`:
1. Den opretter et tomt array `shooters`:

```js
let shooters = [];
```
2. Fills it with functions via `shooters.push(function)` in the loop.
2. Fylder den med funktioner via `shooters.push(function)` i løkken.

Every element is a function, so the resulting array looks like this:
Hvert element er en funktion, så det resulterer i et array der ser således ud:

```js no-beautify
shooters = [
Expand All @@ -25,40 +25,38 @@ Let's examine what exactly happens inside `makeArmy`, and the solution will beco
];
```

3. The array is returned from the function.
3. Arrayet returneres fra funktionen.

Then, later, the call to any member, e.g. `army[5]()` will get the element `army[5]` from the array (which is a function) and calls it.
Senere vil et kald til et element i arrayet, f.eks. `army[5]()` vil hente elementet `army[5]` fra arrayet (som er en funktion) og kalde den.

Now why do all such functions show the same value, `10`?
Men, hvorfor viser alle sådanne funktioner så samme værdi, `10`?

That's because there's no local variable `i` inside `shooter` functions. When such a function is called, it takes `i` from its outer lexical environment.
Det skyldes, at der ikke er en lokal variabel `i` inde i `shooter`-funktionerne. Når en sådan funktion kaldes, tager den `i` fra dens ydre leksikale miljø. Så hvad er værdien af `i` når funktionen kaldes?

Then, what will be the value of `i`?

If we look at the source:
Kig på koden:

```js
function makeArmy() {
...
let i = 0;
while (i < 10) {
let shooter = function() { // shooter function
alert( i ); // should show its number
let shooter = function() { // shooter funktion
alert( i ); // skal vise sit nummer
};
shooters.push(shooter); // add function to the array
shooters.push(shooter); // tilføj funktionen til arrayet
i++;
}
...
}
```

We can see that all `shooter` functions are created in the lexical environment of `makeArmy()` function. But when `army[5]()` is called, `makeArmy` has already finished its job, and the final value of `i` is `10` (`while` stops at `i=10`).
Vi kan se at alle `shooter` funktioner er oprettet i det leksikale miljø af `makeArmy()` funktionen. Men når `army[5]()` kaldes, har `makeArmy` allerede afsluttet sin job, og den endelige værdi af `i` er `10` (`while` stopper ved `i=10`).

As the result, all `shooter` functions get the same value from the outer lexical environment and that is, the last value, `i=10`.
Som resultat får alle `shooter` funktioner samme værdi fra det ydre leksikale miljø og det er den sidste værdi, `i=10`.

![](lexenv-makearmy-empty.svg)

As you can see above, on each iteration of a `while {...}` block, a new lexical environment is created. So, to fix this, we can copy the value of `i` into a variable within the `while {...}` block, like this:
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 af `i` til en variabel inden i `while {...}` blokken, som dette:

```js run
function makeArmy() {
Expand All @@ -69,8 +67,8 @@ Let's examine what exactly happens inside `makeArmy`, and the solution will beco
*!*
let j = i;
*/!*
let shooter = function() { // shooter function
alert( *!*j*/!* ); // should show its number
let shooter = function() { // shooter funktion
alert( *!*j*/!* ); // skal vise sit nummer
};
shooters.push(shooter);
i++;
Expand All @@ -81,18 +79,18 @@ Let's examine what exactly happens inside `makeArmy`, and the solution will beco

let army = makeArmy();

// Now the code works correctly
// Nu virker koden som den skal
army[0](); // 0
army[5](); // 5
```

Here `let j = i` declares an "iteration-local" variable `j` and copies `i` into it. Primitives are copied "by value", so we actually get an independent copy of `i`, belonging to the current loop iteration.
Her vil `let j = i` deklarere en lokal variabel `j` og kopiere `i` over i den. Primitiver kopieres "ved deres værdi", så vi får en reelt uafhængig kopi af `i`, der tilhører den aktuelle løkkes iteration.

The shooters work correctly, because the value of `i` now lives a little bit closer. Not in `makeArmy()` Lexical Environment, but in the Lexical Environment that corresponds to the current loop iteration:
Shooters virker korrekt nu fordi værdien af `i` nu lever et lidt tættere på. Ikke i `makeArmy()` Lexical Environment, men i det Lexical Environment der svarer til den aktuelle løkkes iteration:

![](lexenv-makearmy-while-fixed.svg)

Such a problem could also be avoided if we used `for` in the beginning, like this:
Dette problem kunne undgås hvis vi brugte `for` i stedet for `while`, som dette:

```js run demo
function makeArmy() {
Expand All @@ -102,8 +100,8 @@ Let's examine what exactly happens inside `makeArmy`, and the solution will beco
*!*
for(let i = 0; i < 10; i++) {
*/!*
let shooter = function() { // shooter function
alert( i ); // should show its number
let shooter = function() { // shooter funktion
alert( i ); // bør vise sit nummer
};
shooters.push(shooter);
}
Expand All @@ -117,13 +115,12 @@ Let's examine what exactly happens inside `makeArmy`, and the solution will beco
army[5](); // 5
```

That's essentially the same, because `for` on each iteration generates a new lexical environment, with its own variable `i`. So `shooter` generated in every iteration references its own `i`, from that very iteration.
Det er grundlæggende det samme fordi `for` gennem hver iteration genererer en ny leksikale miljø med sin egen variabel `i`. `shooter` genereret i hver iteration refererer til dens egen `i`, fra den pågældende iteration.

![](lexenv-makearmy-for-fixed.svg)

Now, as you've put so much effort into reading this, and the final recipe is so simple - just use `for`, you may wonder -- was it worth that?

Well, if you could easily answer the question, you wouldn't read the solution. So, hopefully this task must have helped you to understand things a bit better.
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?

Besides, there are indeed cases when one prefers `while` to `for`, and other scenarios, where such problems are real.
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.
26 changes: 13 additions & 13 deletions 1-js/06-advanced-functions/03-closure/10-make-army/task.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,40 @@ importance: 5

---

# Army of functions
# En hær af funktioner

The following code creates an array of `shooters`.
Den følgende kode opretter en array af `shooters`.

Every function is meant to output its number. But something is wrong...
Hver funktion er ment at vise sit nummer. Men noget er forkert...

```js run
function makeArmy() {
let shooters = [];

let i = 0;
while (i < 10) {
let shooter = function() { // create a shooter function,
alert( i ); // that should show its number
let shooter = function() { // opret en shooter funktion,
alert( i ); // der skal vise sit nummer
};
shooters.push(shooter); // and add it to the array
shooters.push(shooter); // og tilføj den til arrayet
i++;
}

// ...and return the array of shooters
// ...og returner arrayet af shooters
return shooters;
}

let army = makeArmy();

*!*
// all shooters show 10 instead of their numbers 0, 1, 2, 3...
army[0](); // 10 from the shooter number 0
army[1](); // 10 from the shooter number 1
army[2](); // 10 ...and so on.
// 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.
*/!*
```

Why do all of the shooters show the same value?
Hvorfor får alle shooters samme værdi?

Fix the code so that they work as intended.
Fiks koden så koden virker som forventet.

Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
The answer is: **Pete**.
Svaret er: **Pete**.

The `work()` function in the code below gets `name` from the place of its origin through the outer lexical environment reference:
Funktionen `work()` i koden nedenfor får `name` fra stedet hvor den blev oprettet gennem referencen til det ydre leksikale miljø:

![](lexenv-nested-work.svg)

So, the result is `"Pete"` here.
Så resultatet er `"Pete"` her.

But if there were no `let name` in `makeWorker()`, then the search would go outside and take the global variable as we can see from the chain above. In that case the result would be `"John"`.
Men, hvis der ikke var `let name` i `makeWorker()`, så ville søgningen gå udenfor og tage den globale variabel som vi kan se fra kæden ovenfor. I det tilfælde ville resultatet være `"John"`.
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ importance: 5

---

# Which variables are available?
# Hvilke variable er tilgængelige?

The function `makeWorker` below makes another function and returns it. That new function can be called from somewhere else.
Funktionen `makeWorker` nedenfor opretter en anden funktion og returnerer den. Den nye funktion kan kaldes fra et andet sted.

Will it have access to the outer variables from its creation place, or the invocation place, or both?
Vil den have adgang til de ydre variable fra sit oprettelsessted, eller fra sit kaldested, eller begge?

```js
function makeWorker() {
Expand All @@ -19,11 +19,11 @@ function makeWorker() {

let name = "John";

// create a function
// Opret en funktion
let work = makeWorker();

// call it
work(); // what will it show?
// Kald den
work(); // Hvad vil den vise?
```

Which value it will show? "Pete" or "John"?
Hvilken værdi vil den vise: "Pete" eller "John"?
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
The answer: **0,1.**
Svaret er: **0,1.**

Functions `counter` and `counter2` are created by different invocations of `makeCounter`.
Funktionen `counter` og `counter2` er skabt af forskellige kald af `makeCounter`.

So they have independent outer Lexical Environments, each one has its own `count`.
Så de har uafhængige ydre leksikale miljøer, hver har sin egen `count`.
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ importance: 5

---

# Are counters independent?
# Er tællere uafhængige?

Here we make two counters: `counter` and `counter2` using the same `makeCounter` function.
Her opretter vi to tællere: `counter` og `counter2` ved brug af samme `makeCounter` funktion.

Are they independent? What is the second counter going to show? `0,1` or `2,3` or something else?
Er de uafhængige? Hvad vil den anden tæller vise? `0,1` eller `2,3` eller noget andet?

```js
function makeCounter() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Surely it will work just fine.
Det vil fungere helt fint.

Both nested functions are created within the same outer Lexical Environment, so they share access to the same `count` variable:
Begge indlejrede funktioner er oprettet i det samme ydre leksikale miljø, så de deler adgang til den samme `count` variabel:

```js run
function Counter() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ importance: 5

---

# Counter object
# Counter objekt

Here a counter object is made with the help of the constructor function.
Her er et counter objekt oprettet ved hans hjælp af constructor funktionen.

Will it work? What will it show?
Hvordan vil det fungere? Hvad vil det vise?

```js
function Counter() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
The result is **an error**.
Resultatet er **en fejl**.

The function `sayHi` is declared inside the `if`, so it only lives inside it. There is no `sayHi` outside.
Funktionen `sayHi` er deklareret inden for `if`, så den lever kun inden for det. Der er ingen `sayHi` uden for `if`.
Loading