Skip to content

Commit c48c52c

Browse files
authored
Merge pull request #2127 from MuhammedZakir/master
Improve make-army task
2 parents 894e1b5 + fdf8eb3 commit c48c52c

File tree

1 file changed

+96
-97
lines changed
  • 1-js/06-advanced-functions/03-closure/10-make-army

1 file changed

+96
-97
lines changed

1-js/06-advanced-functions/03-closure/10-make-army/solution.md

+96-97
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Let's examine what exactly happens inside `makeArmy`, and the solution will beco
66
```js
77
let shooters = [];
88
```
9-
2. Fills it in the loop via `shooters.push(function...)`.
9+
2. Fills it with functions via `shooters.push(function)` in the loop.
1010

1111
Every element is a function, so the resulting array looks like this:
1212

@@ -26,105 +26,104 @@ Let's examine what exactly happens inside `makeArmy`, and the solution will beco
2626
```
2727

2828
3. The array is returned from the function.
29-
30-
Then, later, the call to any member, e.g. `army[5]()` will get the element `army[5]` from the array (that's a function) and call it.
31-
32-
Now why do all such functions show the same value, `10`?
33-
34-
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.
35-
36-
What will be the value of `i`?
37-
38-
If we look at the source:
39-
40-
```js
41-
function makeArmy() {
42-
...
43-
let i = 0;
44-
while (i < 10) {
45-
let shooter = function() { // shooter function
46-
alert( i ); // should show its number
47-
};
48-
...
49-
}
50-
...
51-
}
52-
```
53-
54-
We can see that all `shooter` functions are created in the lexical environment, associated with the one `makeArmy()` run. But when `army[5]()` is called, `makeArmy` has already finished its job, and the final value of `i` is `10`(`while` finishes at `10`).
55-
56-
As the result, all `shooter` functions get the same value from the outer lexical environment and that is, the last value, `i=10`.
57-
58-
![](lexenv-makearmy-empty.svg)
59-
60-
As you can see above, on each iteration of a `while {...} ` block, a new lexical environment is created.
61-
62-
So, to fix a problem we can copy the value of `i` into a variable within the `while {...}` block, like this:
63-
64-
```js run
65-
function makeArmy() {
66-
let shooters = [];
67-
68-
let i = 0;
69-
while (i < 10) {
29+
30+
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.
31+
32+
Now why do all such functions show the same value, `10`?
33+
34+
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.
35+
36+
Then, what will be the value of `i`?
37+
38+
If we look at the source:
39+
40+
```js
41+
function makeArmy() {
42+
...
43+
let i = 0;
44+
while (i < 10) {
45+
let shooter = function() { // shooter function
46+
alert( i ); // should show its number
47+
};
48+
shooters.push(shooter); // add function to the array
49+
i++;
50+
}
51+
...
52+
}
53+
```
54+
55+
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`).
56+
57+
As the result, all `shooter` functions get the same value from the outer lexical environment and that is, the last value, `i=10`.
58+
59+
![](lexenv-makearmy-empty.svg)
60+
61+
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:
62+
63+
```js run
64+
function makeArmy() {
65+
let shooters = [];
66+
67+
let i = 0;
68+
while (i < 10) {
69+
*!*
70+
let j = i;
71+
*/!*
72+
let shooter = function() { // shooter function
73+
alert( *!*j*/!* ); // should show its number
74+
};
75+
shooters.push(shooter);
76+
i++;
77+
}
78+
79+
return shooters;
80+
}
81+
82+
let army = makeArmy();
83+
84+
// Now the code works correctly
85+
army[0](); // 0
86+
army[5](); // 5
87+
```
88+
89+
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.
90+
91+
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 the current loop iteration:
92+
93+
![](lexenv-makearmy-while-fixed.svg)
94+
95+
Such problem could also be avoided if we used `for` in the beginning, like this:
96+
97+
```js run demo
98+
function makeArmy() {
99+
100+
let shooters = [];
101+
70102
*!*
71-
let j = i;
103+
for(let i = 0; i < 10; i++) {
72104
*/!*
73-
let shooter = function() { // shooter function
74-
alert( *!*j*/!* ); // should show its number
75-
};
76-
shooters.push(shooter);
77-
i++;
78-
}
79-
80-
return shooters;
81-
}
82-
83-
let army = makeArmy();
84-
85-
// Now the code works correctly
86-
army[0](); // 0
87-
army[5](); // 5
88-
```
89-
90-
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.
91-
92-
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 the current loop iteration:
93-
94-
![](lexenv-makearmy-while-fixed.svg)
95-
96-
Such problem could also be avoided if we used `for` in the beginning, like this:
97-
98-
```js run demo
99-
function makeArmy() {
100-
101-
let shooters = [];
102-
103-
*!*
104-
for(let i = 0; i < 10; i++) {
105-
*/!*
106-
let shooter = function() { // shooter function
107-
alert( i ); // should show its number
108-
};
109-
shooters.push(shooter);
110-
}
111-
112-
return shooters;
113-
}
114-
115-
let army = makeArmy();
116-
117-
army[0](); // 0
118-
army[5](); // 5
119-
```
120-
121-
That's essentially, the same, as `for` on each iteration generates the new lexical environment, with its own variable `i`. So `shooter` generated in every iteration references its own `i`, from that very iteration.
122-
123-
![](lexenv-makearmy-for-fixed.svg)
105+
let shooter = function() { // shooter function
106+
alert( i ); // should show its number
107+
};
108+
shooters.push(shooter);
109+
}
110+
111+
return shooters;
112+
}
113+
114+
let army = makeArmy();
115+
116+
army[0](); // 0
117+
army[5](); // 5
118+
```
119+
120+
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.
121+
122+
![](lexenv-makearmy-for-fixed.svg)
124123
125-
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?
124+
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?
126125
127-
Well, if you could easily answer the question of that task, you wouldn't read the solution, so hopefully this task must have helped you to understand things a bit better.
126+
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.
128127
129-
Besides, there are indeed cases when one prefers `while` to `for`, and other scenarios where such problems are real.
128+
Besides, there are indeed cases when one prefers `while` to `for`, and other scenarios, where such problems are real.
130129

0 commit comments

Comments
 (0)