He entendido por qué la salida de este código debería ser 3 3 3
.
for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 1); }
Sin embargo, no puedo entender por qué la salida de este código es 0 1 2
.
for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 1); }
Quiero más claridad con la salida del segundo bucle for
.
Hay una diferencia de alcance con let
y var
, lo que hace que se comporten de manera diferente.
Aquí hay una cita de otrarespuesta de Stack Overflow sobre las diferencias entre var
y let
.
La principal diferencia son las reglas de alcance. Las variables declaradas por la palabra clave
var
tienen como alcance el cuerpo de la función inmediata (de ahí el alcance de la función), mientras que las variableslet
tienen como alcance el bloque envolvente inmediato indicado por{ }
(de ahí el alcance del bloque).
Entonces, en resumen, var
se refiere a la misma dirección de variable. Pero, como let
tiene un alcance bloqueado (según la cita anterior), cada devolución de llamada en setTimeout()
hará i
tenga un valor diferente al anterior.
Un experimento posible es hacer que let
se comporte como var
. Para hacer let
se comporte como var
, puede usar (y ejecutar) el siguiente código.
Para ver cómo se comporta let
como var
, lea más adelante sobre la elevación de var
.
let i; for (i = 0; i < 3; i++) { setTimeout(() => console.log(i), 1); }
Cuando pasamos al ámbito principal, se comporta como var
(ya let
var
se coloca en la parte superior del archivo con un valor undefined
en tiempo de ejecución).
Esto hace que la variable let
se comporte como var
, haciendo que la salida del ciclo for
sea igual que si la variable let
fuera var
.
¡Gracias a Nick Vu por el experimento!
Aquí hay una descripción general rápida de la elevación de var
a continuación.
El código real (que se muestra a continuación):
web = "stackoverflow.com"; var web;
se entiende como:
var web; web = "stackoverflow.com";
Básicamente, definir una variable con var
en cualquier lugar siempre dará como resultado que esté en la parte superior del archivo, lo que resultará en un comportamiento extraño con el bucle for
.
Esta es la razón por la que muchas personas prefieren let
over var
; ¡Por lo confuso que puede ser el levantamiento de var
!
¡Siempre use let
, a menos que var
sea absolutamente necesario!
El primer ciclo var i
se eleva y siempre se refiere a solo 1 variable durante ese ciclo, setTimeout
pospondrá su console.log
con la pila de llamadas , por eso el valor es 3
all. Así es como se construye var i
con elevación debajo del capó.
var i; //hoisted //`var` in `for` gets removed for (i = 0; i < 3; i++) { setTimeout(() => console.log(i), 1); }
Pero let i
diferente, el valor i
se inicializará bajo ese alcance de bloque en iteración cada vez, por eso el resultado es 0 1 2
.
Para un experimento, hacer let
tiene un resultado similar al del ciclo var
.
let i; //move `i` to the upper block scope for (i = 0; i < 3; i++) { setTimeout(() => console.log(i), 1); //all logs will share the same `i` }