Merge pull request #2127 from MuhammedZakir/master
Improve make-army task
This commit is contained in:
commit
c48c52cb81
1 changed files with 96 additions and 97 deletions
|
@ -6,7 +6,7 @@ Let's examine what exactly happens inside `makeArmy`, and the solution will beco
|
|||
```js
|
||||
let shooters = [];
|
||||
```
|
||||
2. Fills it in the loop via `shooters.push(function...)`.
|
||||
2. Fills it with functions via `shooters.push(function)` in the loop.
|
||||
|
||||
Every element is a function, so the resulting array looks like this:
|
||||
|
||||
|
@ -27,42 +27,41 @@ Let's examine what exactly happens inside `makeArmy`, and the solution will beco
|
|||
|
||||
3. The array is returned from the function.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
Now why do all such functions show the same value, `10`?
|
||||
Now why do all such functions show the same value, `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.
|
||||
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.
|
||||
|
||||
What will be the value of `i`?
|
||||
Then, what will be the value of `i`?
|
||||
|
||||
If we look at the source:
|
||||
If we look at the source:
|
||||
|
||||
```js
|
||||
function makeArmy() {
|
||||
```js
|
||||
function makeArmy() {
|
||||
...
|
||||
let i = 0;
|
||||
while (i < 10) {
|
||||
let shooter = function() { // shooter function
|
||||
alert( i ); // should show its number
|
||||
};
|
||||
...
|
||||
shooters.push(shooter); // add function to the array
|
||||
i++;
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
}
|
||||
```
|
||||
|
||||
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`).
|
||||
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`).
|
||||
|
||||
As the result, all `shooter` functions get the same value from the outer lexical environment and that is, the last value, `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`.
|
||||
|
||||

|
||||

|
||||
|
||||
As you can see above, on each iteration of a `while {...} ` block, a new lexical environment is created.
|
||||
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:
|
||||
|
||||
So, to fix a problem we can copy the value of `i` into a variable within the `while {...}` block, like this:
|
||||
|
||||
```js run
|
||||
function makeArmy() {
|
||||
```js run
|
||||
function makeArmy() {
|
||||
let shooters = [];
|
||||
|
||||
let i = 0;
|
||||
|
@ -78,31 +77,31 @@ function makeArmy() {
|
|||
}
|
||||
|
||||
return shooters;
|
||||
}
|
||||
}
|
||||
|
||||
let army = makeArmy();
|
||||
let army = makeArmy();
|
||||
|
||||
// Now the code works correctly
|
||||
army[0](); // 0
|
||||
army[5](); // 5
|
||||
```
|
||||
// Now the code works correctly
|
||||
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.
|
||||
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.
|
||||
|
||||
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:
|
||||
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:
|
||||
|
||||

|
||||

|
||||
|
||||
Such problem could also be avoided if we used `for` in the beginning, like this:
|
||||
Such problem could also be avoided if we used `for` in the beginning, like this:
|
||||
|
||||
```js run demo
|
||||
function makeArmy() {
|
||||
```js run demo
|
||||
function makeArmy() {
|
||||
|
||||
let shooters = [];
|
||||
|
||||
*!*
|
||||
*!*
|
||||
for(let i = 0; i < 10; i++) {
|
||||
*/!*
|
||||
*/!*
|
||||
let shooter = function() { // shooter function
|
||||
alert( i ); // should show its number
|
||||
};
|
||||
|
@ -110,21 +109,21 @@ function makeArmy() {
|
|||
}
|
||||
|
||||
return shooters;
|
||||
}
|
||||
}
|
||||
|
||||
let army = makeArmy();
|
||||
let army = makeArmy();
|
||||
|
||||
army[0](); // 0
|
||||
army[5](); // 5
|
||||
```
|
||||
army[0](); // 0
|
||||
army[5](); // 5
|
||||
```
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||

|
||||

|
||||
|
||||
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?
|
||||
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 of that task, you wouldn't read the solution, so hopefully this task must have helped you to understand things a bit better.
|
||||
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.
|
||||
|
||||
Besides, there are indeed cases when one prefers `while` to `for`, and other scenarios where such problems are real.
|
||||
Besides, there are indeed cases when one prefers `while` to `for`, and other scenarios, where such problems are real.
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue