108

La diferencia entre let y var en Javascript

2351Puntos

hace 5 años

Te comparto una pregunta que suele hacerse en entrevistas de JavaScript. Pone en prueba tu conocimiento sobre scope (ámbito), closures (clausuras) y la diferencia entre var y let.

La función printNumbers debe imprimir los números del 0 al 9 en la consola. Sin embargo, cuando intentas ejecutar este código, solo se imprime el número 10 (10 veces). ¿Por qué?

function printNumbers() {
	for (var i = 0; i < 10; i++) {
		setTimeout(
      function printer() {
	      console.log(i);
	    },
			100 * i
		);
	}
}

pr****intNumbers();

¡Inténtalo tu mismo! Abre una consola de JavaScript y copia y pega el código.

Comencemos estudiando la ejecución esperada. Primero llamamos la función. La función comienza con un for-loop que comienza en 0 y se detiene cuando i es mayor o igual que 10. Cada vez que se ejecuta el loop, se agenda una función que será ejecutada en 100*i milisegundos.

Es función tiene una tarea muy fácil. Imprimir con en la consola un número. Y sin embargo, no lo hace. Peor aún, solo imprime el número 10 diez veces.

Es curioso que sea el número 10. Este el valor que i alcanza cuando el loop se deja de ejecutar. Pareciera entonces que la función se está recordando de i en ese momento. Esto sucede por que el scope de i está contenido dentro de la función printNumbers.

Para entender mejor, escribamos una versión equivalente del mismo código.

function printNumbers() {
	var i;

	for (i = 0; i < 10; i++) {
		setTimeout(
      function printer() {
	      console.log(i);
	    },
			100 * i
		);
	}
}

printNumbers();

Nota como la declaración de i está fuera del for-loop. Esto es válido por que las variables declaradas con var obtienen “function scope”. Es decir, están disponibles dentro de todo el bloque de la función donde fueron declaradas.

Observa que todas las funciones que se ejecutan en el setTimeout guardan una referencia a i. Si las funciones ejecutaran inmediatamente, el valor de i sería el esperado. Pero como la ejecución ocurrirá eventualmente, el valor de i entonces será el último valor que obtuvo.

Para arreglar el bug, necesitamos que el valor que se vaya a imprimir viva dentro de un scope que no se comparta entre todas las funciones printer. Esto lo podemos lograr utilizando let en lugar de var.

function printNumbers() {
	for (let i = 0; i < 10; i++) {
		// Cuando usamos let en un for-loop, es como si definieramos `i` aquí.

		setTimeout(
      function printer() {
	      console.log(i);
	    },
			100 * i
		);
	}
}

printNumbers();

let tiene un scope llamado “block scope”. Su valor es contenido dentro del bloque de código actual, o sea, el area entre { y }. Lo que sucede en está versión del código es que en cada loop i es independiente, es decir, es como si estuviera definida en la primera línea del bloque del for-loop.

Siendo así, cada i que se utiliza en printer es distinta. Por esta razón el valor no se comparte y obtenemos el valor que esperamos.

Es importante entender cual es el scope de las variables en nuestro código. No tener claro cómo funciona puede introducir bugs difíciles de encontrar y arreglar.

Junto al tema de scope, los closures son muy importante entenderlos. Para aprender más de ellos, profundizar tu conociemiento de scope, y de otros conceptos avanzados de JavaScript, te invito a tomar el Curso Profesional de JavaScript donde te enseño todo esto y más.

Richard B.
Richard B.
richard

2351Puntos

hace 5 años

Todas sus entradas
Escribe tu comentario
+ 2
Ordenar por:
12
17251Puntos

Una explicación sencilla y entendible. Por esto es que yo siempre uso let en lugares donde van a haber ciclos activos en mi código. Su valor es muy importante y no debe ser alterado. Conocer este tipo de conceptos son importantes para cualquier desarrollador en JavaScript. ¡Gracias Richard por ser nuestro profesor de JavaScript!

8
19857Puntos

Sigo sin entender por qué se repite el 10 😦

4
35712Puntos

Gracias un articulo aqui el link (https://cybmeta.com/var-let-y-const-en-javascript) entiendo mi manera que sucede con usar var dentro de un ciclo for.

Lo primero es entender la definición hoisting(«izamiento» o «levantamiento»): la declaración de una variable (var) es «levantada» hasta el inicio del ámbito de aplicación (en este caso a su ámbito fuera del ciclo for o cualquier función) pero la asignación al valor permanece en el sitio donde se realice. Mientras let no tiene hoisting, su valor permanece dentro de la iteración, miralo como una pila de eventos.

En el primer caso usando var la variable i realiza el ciclo for, del 1 al 10, y se hiza el valor al contexto global i=10, y al existir un delay en el console.log terminamos imprimiento en consola 10 veces el mismo resultado.

console.log(10)
console.log(10)
console.log(10)…

En el segundo caso let solo vive en su contexto local, por lo que al usar cada iteración a mi forma particular de entender permite que cada console.log se ejecute “recordando” su valor anterior.

console.log(1)
console.log(2)
console.log(3)…

o al menos es la mi humilde punto de verlo, recomiendo leer el articulo par complementar todos los comentarios y el post.

4
1820Puntos

Sutil pero gran diferencia.

4
260Puntos

Muy interesante, practico y fácil de analizar.

4

Buen trabajo muy bien explicado!

3

La otra vez estaba leyendo del tema y para ser sincero no me quedo muy claro, ahora con este Post entiendo muy bien la diferencia entre var y let

3
15832Puntos

Excelente explicación, corta y directa.

3
1628Puntos

Muy bien explicado, hasta ahora no tenia bien claro cuando usar let y la verdad es que siempre habia usado var, Muchas Gracias!!!

2
5549Puntos

Me tomo tiempo entender la explicacion, y es que es muy importante conocer que setTimeout() No detiene la ejecucion por X tiempo, solo agenda la ejecucion de una funcion “de forma paralela” para ese tiempo establecido, es por esto que despues de X tiempo ya el ciclo for se ha recorrido completo, llegando a asignar a i = 10, por lo que cuando se ejecuta printer (10 veces) , ya var i tiene 10.

2
9347Puntos

Buen tema no lo tenia en cuenta!!!

1
31820Puntos

Que bien hoy aprendí la diferencia entre var y let.

1

Disculpa no entendí la lógica de " 100*i ", no se a que se refiere

1
16394Puntos
1.jpg

En la explicación el instructor menciona que “i” se va detener cuando sea mayor o igual que 10, pero en la función se ha escrito de la siguiente manera i <10, para que se mayor igual a 10 no debió escribirse de esta forma: i <= 10?

1

Otra forma de hacerlo sin usar let es usando closures:

functionprinter(i)
{
	returnfunction()
	{
		console.log(i);
	}
}

functionprintNumbers() 
{
	for ( var i = 0; i < 10; i++) 
	{
		setTimeout(printer(i),100 * i);
	}
}

Pero no es nada recomendado, mejor es usar let.

1
20960Puntos

Que importante es saber el uso de aplicacion de var y let. esto cambia contundentemente la ejecucion del codigo.

0
10423Puntos
<strong>Genial</strong>```
0

Estuve un rato buscando la explicación, llegue por el aporte de un compañero, gracias a todos!!

0
59454Puntos

Bastante útil la explicación si no se ha tomado aun el curso mencionado gracias.