# Что происходит в этом коде
Функция `makeArmy` делает следующее:
- Создаёт пустой массив `shooter`:
```js
var shooters = [];
```
- В цикле заполняет массив элементами через `shooter.push`.
При этом каждый элемент массива -- это функция, так что в итоге после цикла массив будет таким:
```js
//+ no-beautify
shooters = [
function () { alert(i); },
function () { alert(i); },
function () { alert(i); },
function () { alert(i); },
function () { alert(i); },
function () { alert(i); },
function () { alert(i); },
function () { alert(i); },
function () { alert(i); },
function () { alert(i); }
];
```
Этот массив возвращается из функции.
- Вызов `army[5]()` -- это получение элемента массива (им будет функция), и тут же -- её запуск.
# Почему ошибка
Вначале разберемся, почему все стрелки выводят одно и то же значение.
В функциях-стрелках `shooter` отсутствует переменная `i`. Когда такая функция вызывается, то `i` она берет из внешнего `LexicalEnvironment`.
Чему же будет равно это значение `i`?
К моменту вызова `army[0]()`, функция `makeArmy` уже закончила работу. Цикл завершился, последнее значение было `i=10`.
В результате все функции `shooter` получают из внешнего лексического кружения это, одно и то же, последнее, значение `i=10`.
Попробуйте исправить проблему самостоятельно.
# Исправление (3 варианта)
Есть несколько способов исправить ситуацию.
- **Первый способ исправить код - это привязать значение непосредственно к функции-стрелку:**
```js
//+ run
function makeArmy() {
var shooters = [];
for (var i = 0; i < 10; i++) {
*!*
var shooter = function me() {
alert( me.i );
};
shooter.i = i;
*/!*
shooters.push(shooter);
}
return shooters;
}
var army = makeArmy();
army[0](); // 0
army[1](); // 1
```
В этом случае каждая функция хранит в себе свой собственный номер.
Кстати, обратите внимание на использование Named Function Expression, вот в этом участке:
```js
...
var shooter = function me() {
alert( me.i );
};
...
```
Если убрать имя `me` и оставить обращение через `shooter`, то работать не будет:
```js
for (var i = 0; i < 10; i++) {
var shooter = function() {
*!*
alert( shooter.i ); // вывести свой номер (не работает!)
// потому что откуда функция возьмёт переменную shooter?
// ..правильно, из внешнего объекта, а там она одна на всех
*/!*
};
shooter.i = i;
shooters.push(shooter);
}
```
Вызов `alert(shooter.i)` при вызове будет искать переменную `shooter`, а эта переменная меняет значение по ходу цикла, и к моменту вызову она равна последней функции, созданной в цикле.
Если использовать Named Function Expression, то имя жёстко привязывается к конкретной функции, и поэтому в коде выше `me.i` возвращает правильный `i`.
- **Другое, более продвинутое решение -- использовать дополнительную функцию для того, чтобы "поймать" текущее значение `i`**:
```js
//+ run
function makeArmy() {
var shooters = [];
for (var i = 0; i < 10; i++) {
*!*
var shooter = (function(x) {
return function() {
alert( x );
};
})(i);
*/!*
shooters.push(shooter);
}
return shooters;
}
var army = makeArmy();
army[0](); // 0
army[1](); // 1
```
Посмотрим выделенный фрагмент более внимательно, чтобы понять, что происходит:
```js
var shooter = (function(x) {
return function() {
alert( x );
};
})(i);
```
Функция `shooter` создана как результат вызова промежуточного функционального выражения `function(x)`, которое объявляется -- и тут же выполняется, получая `x = i`.
Так как `function(x)` тут же завершается, то значение `x` больше не меняется. Оно и будет использовано в возвращаемой функции-стрелке.
Для красоты можно изменить название переменной `x` на `i`, суть происходящего при этом не изменится:
```js
var shooter = (function(i) {
return function() {
alert( i );
};
})(i);
```
**Кстати, обратите внимание -- скобки вокруг `function(i)` не нужны**, можно и так:
```js
var shooter = function(i) { // *!*без скобок вокруг function(i)*/!*
return function() {
alert( i );
};
}(i);
```
Скобки добавлены в код для лучшей читаемости, чтобы человек, который просматривает его, не подумал, что `var shooter = function`, а понял что это вызов "на месте", и присваивается его результат.
- **Еще один забавный способ - обернуть весь цикл во временную функцию**:
```js
//+ run
function makeArmy() {
var shooters = [];
*!*
for (var i = 0; i < 10; i++)(function(i) {
var shooter = function() {
alert( i );
};
shooters.push(shooter);
})(i);
*/!*
return shooters;
}
var army = makeArmy();
army[0](); // 0
army[1](); // 1
```
Вызов `(function(i) { ... })` обернут в скобки, чтобы интерпретатор понял, что это `Function Expression`.
Плюс этого способа - в большей читаемости. Фактически, мы не меняем создание `shooter`, а просто обертываем итерацию в функцию.