diff --git a/1-js/06-advanced-functions/03-closure/10-make-army/solution.md b/1-js/06-advanced-functions/03-closure/10-make-army/solution.md index 2e3d54c19f..6adeec6b46 100644 --- a/1-js/06-advanced-functions/03-closure/10-make-army/solution.md +++ b/1-js/06-advanced-functions/03-closure/10-make-army/solution.md @@ -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: @@ -26,105 +26,104 @@ 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. - -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. - -What will be the value of `i`? - -If we look at the source: - -```js -function makeArmy() { - ... - let i = 0; - while (i < 10) { - let shooter = function() { // shooter function - alert( i ); // should show its number - }; - ... - } - ... -} -``` - -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`). - -As the result, all `shooter` functions get the same value from the outer lexical environment and that is, the last value, `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 a problem we can copy the value of `i` into a variable within the `while {...}` block, like this: - -```js run -function makeArmy() { - let shooters = []; - - let i = 0; - while (i < 10) { + + 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`? + + 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. + + Then, what will be the value of `i`? + + If we look at the source: + + ```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 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`. + + ![](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: + + ```js run + function makeArmy() { + let shooters = []; + + let i = 0; + while (i < 10) { + *!* + let j = i; + */!* + let shooter = function() { // shooter function + alert( *!*j*/!* ); // should show its number + }; + shooters.push(shooter); + i++; + } + + return shooters; + } + + let army = makeArmy(); + + // 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. + + 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: + + ![](lexenv-makearmy-while-fixed.svg) + + Such problem could also be avoided if we used `for` in the beginning, like this: + + ```js run demo + function makeArmy() { + + let shooters = []; + *!* - let j = i; + for(let i = 0; i < 10; i++) { */!* - let shooter = function() { // shooter function - alert( *!*j*/!* ); // should show its number - }; - shooters.push(shooter); - i++; - } - - return shooters; -} - -let army = makeArmy(); - -// 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. - -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: - -![](lexenv-makearmy-while-fixed.svg) - -Such problem could also be avoided if we used `for` in the beginning, like this: - -```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 - }; - shooters.push(shooter); - } - - return shooters; -} - -let army = makeArmy(); - -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. - -![](lexenv-makearmy-for-fixed.svg) + let shooter = function() { // shooter function + alert( i ); // should show its number + }; + shooters.push(shooter); + } + + return shooters; + } + + let army = makeArmy(); + + army[0](); // 0 + 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. + + ![](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? +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.