Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 51 additions & 51 deletions 1-js/06-advanced-functions/03-closure/10-make-army/solution.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,89 +37,89 @@ Examinemos lo que sucede dentro de `makeArmy`, y la solución será obvia.

Si miramos la fuente:

```js
function makeArmy() {
...
let i = 0;
while (i < 10) {
let shooter = function() { // shooter function
```js
function makeArmy() {
...
let i = 0;
while (i < 10) {
let shooter = function() { // shooter function
alert( i ); // debería mostrar su número
};
};
shooters.push(shooter); // agrega la función al array
i++;
}
...
}
```
}
...
}
```

Podemos ver que todas las funciones `shooter` están creadas en el ambiente léxico asociado a la ejecución de `makeArmy()`. Pero cuando se llama a `army[5]()`, `makeArmy` ya ha terminado su trabajo, y el valor final de `i` es `10` (`while` finaliza en `i=10`).

Como resultado, todas las funciones `shooter` obtienen el mismo valor del mismo entorno léxico externo, que es el último valor `i=10`.

![](lexenv-makearmy-empty.svg)
![](lexenv-makearmy-empty.svg)

Como puedes ver arriba, con cada iteración del bloque `while {...}` un nuevo ambiente léxico es creado. Entonces, para corregir el problema podemos copiar el valor de `i` en una variable dentro del bloque `while {...}` como aquí:

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

let i = 0;
while (i < 10) {
*!*
let j = i;
*/!*
let shooter = function() { // shooter function
alert( *!*j*/!* ); // debería mostrar su número
};
shooters.push(shooter);
i++;
}
```js run
function makeArmy() {
let shooters = [];

let i = 0;
while (i < 10) {
*!*
let j = i;
*/!*
let shooter = function() { // shooter function
alert( *!*j*/!* ); // debería mostrar su número
};
shooters.push(shooter);
i++;
}

return shooters;
}
return shooters;
}

let army = makeArmy();
let army = makeArmy();

// Ahora el código funciona correctamente
army[0](); // 0
army[5](); // 5
```
army[0](); // 0
army[5](); // 5
```

Aquí `let j = i` declara una variable de iteración local `j` y copia `i` en ella. Las primitivas son copiadas por valor, así que realmente obtenemos una copia independiente de `i`, perteneciente a la iteración del bucle actual.

Los shooters funcionan correctamente, porque el valor de `i` ahora vive más cerca. No en el ambiente léxico de `makeArmy()` sino en el que corresponde a la iteración del bucle actual:

![](lexenv-makearmy-while-fixed.svg)
![](lexenv-makearmy-while-fixed.svg)

Tal problema habría sido evitado si hubiéramos usado `for` desde el principio:

```js run demo
function makeArmy() {
```js run demo
function makeArmy() {

let shooters = [];
let shooters = [];

*!*
for(let i = 0; i < 10; i++) {
*/!*
let shooter = function() { // shooter function
*!*
for(let i = 0; i < 10; i++) {
*/!*
let shooter = function() { // shooter function
alert( i ); // debería mostrar su número
};
shooters.push(shooter);
}
};
shooters.push(shooter);
}

return shooters;
}
return shooters;
}

let army = makeArmy();
let army = makeArmy();

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

Esto es esencialmente lo mismo, ya que cada iteración de `for` genera un nuevo ambiente léxico con su propia variable `i`. Así el `shooter` generado en cada iteración hace referencia a su propio `i`, de esa misma iteración.

![](lexenv-makearmy-for-fixed.svg)
![](lexenv-makearmy-for-fixed.svg)

Ahora, como has puesto mucho esfuerzo leyendo esto, y la receta final es tan simple: simplemente usa `for`, puede que te preguntes: ¿valió la pena?

Expand Down