En el siguiente fragmento, la función que se pasa a setTimeout forma un cierre con la variable i que está presente en el alcance del script . Entonces la función contiene una referencia a la variable i . El valor de i se actualiza a 5 antes de que i se registre en la consola. Y la salida es: 5 5 5 5 5 (con líneas nuevas)
script.js :
let i; for (i = 0; i < 5; ++i) { setTimeout(() => { console.log(i); }, i * 1000); } La parte anterior me queda clara. Pero cuando declaro la variable i dentro for bucle for, entonces i tiene un alcance de bloque para el bucle for .
for (let i = 0; i < 5; ++i) { setTimeout(() => { console.log(i); }, i * 1000); } En este fragmento de código, espero que el resultado sea el mismo que el del primer fragmento, porque el cierre contiene una referencia a la variable i y el valor de esa referencia debe actualizarse a 5 antes de que el valor se registre en la consola. Pero la salida real es: 0 1 2 3 4 (con líneas nuevas)
La pregunta es, ¿por qué el código en el segundo fragmento se comporta de esta manera?
Es porque:
i en la memoria para cada iteración del ciclo for y la copia anterior se recolecta como basura? No estoy seguro de cómo funciona la administración de memoria para los bucles.i , ¿el valor de i se almacena en el cierre? No creo que este sea el caso.Por favor ayuda para aclarar. ¡Gracias!
Su pregunta menciona algunos detalles de implementación. Sin embargo, estos detalles de implementación no importan al responder su pregunta.
De hecho, tiene razón en que se está produciendo un comportamiento específico del bucle for. Veamos el estándar ECMAScript 2021 aquí:
Cuando se evalúa su ciclo for con let , estos pasos se realizan primero:
Los pasos 4, 9 y 10 son particularmente interesantes para nosotros. El paso 4 reúne todas las variables const y let declaradas en el ciclo for.
El paso 9 establece perIterationLets en las variables let (si hay variables let) o en la lista vacía de lo contrario. El paso 10 luego llama a ForBodyE Evaluation para ejecutar el ciclo:
Aquí, concentrémonos en 3.e y echemos un vistazo más de cerca a CreatePerIterationEnvironment :
Siguiendo cuidadosamente la jerarquía de invocación, observamos que perIterationBindings es la lista perIterationLets de let variables que reunimos antes.
En las líneas ei, e.ii, e.iii, ahora creamos un nuevo enlace (piense en esto como la ubicación de memoria donde viven las variables) y copiamos el valor de la variable con el mismo nombre de la iteración anterior. Sin embargo, es una nueva variable diferente , ¡incluso si tiene el mismo nombre!
Por lo tanto, si un cierre dentro del cuerpo de un bucle captura el contexto actual, las variables del bucle no cambiarán, porque cada iteración del cuerpo del bucle tiene su propio contexto actual .