돌아가기

## 함수를 사용해 군대 만들기

중요도: 5

아래 코드는 `shooters`가 요소인 배열을 만들어줍니다.

모든 함수는 몇 번째 shooter인지 출력해줘야 하는데 뭔가 잘못되었습니다.

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

let i = 0;
while (i < 10) {
let shooter = function() { // shooter 함수
alert( i ); // 몇 번째 shooter인지 출력해줘야 함
};
shooters.push(shooter);
i++;
}

return shooters;
}

let army = makeArmy();

army[0](); // 0번째 shooter가 10을 출력함
army[5](); // 5번째 shooter 역시 10을 출력함
// 모든 shooter가 자신의 번호 대신 10을 출력하고 있음``````

왜 모든 shooter가 동일한 숫자를 출력하는 걸까요? 제대로 된 번호가 출력될 수 있게 코드를 수정해 보세요.

테스트 코드가 담긴 샌드박스를 열어 정답을 작성해보세요.

Let’s examine what’s done inside `makeArmy`, and the solution will become obvious.

1. It creates an empty array `shooters`:

``let shooters = [];``
2. Fills it in the loop via `shooters.push(function...)`.

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

``````shooters = [
];``````
3. The array is returned from the function.

Then, later, the call to `army[5]()` will get the element `army[5]` from the array (it will be a function) and call it.

Now why all such functions show the same?

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:

``````function makeArmy() {
...
let i = 0;
while (i < 10) {
let shooter = function() { // shooter function
alert( i ); // should show its number
};
...
}
...
}``````

…We can see that it lives in the lexical environment associated with the current `makeArmy()` run. But when `army[5]()` is called, `makeArmy` has already finished its job, and `i` has the last value: `10` (the end of `while`).

As a result, all `shooter` functions get from the outer lexical envrironment the same, last value `i=10`.

We can fix it by moving the variable definition into the loop:

``````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``````

Now it works correctly, because every time the code block in `for (let i=0...) {...}` is executed, a new Lexical Environment is created for it, with the corresponding variable `i`.

So, 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. That’s why now it works.

Here we rewrote `while` into `for`.

Another trick could be possible, let’s see it for better understanding of the subject:

``````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();

army[0](); // 0
army[5](); // 5``````

The `while` loop, just like `for`, makes a new Lexical Environment for each run. So here we make sure that it gets the right value for a `shooter`.

We copy `let j = i`. This makes a loop body local `j` and copies the value of `i` to it. Primitives are copied “by value”, so we actually get a complete independent copy of `i`, belonging to the current loop iteration.

테스트 코드가 담긴 샌드박스를 열어 정답을 확인해보세요.