76

La diferencia entre let y var en Javascript

2271Puntos

hace 2 años

Curso Profesional de JavaScript
Curso Profesional de JavaScript

Curso Profesional de JavaScript

Mejora tus habilidades en Javascript. Conoce Typescript y cómo puedes ocuparlo para mejorar el control de tus variables. Comprende conceptos avanzados que te permitan plantear mejores soluciones en tu código. Conoce las APIs del DOM y descubre cómo puedes organizar mejor tu código utilizando patrones de diseño.

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.

Curso Profesional de JavaScript
Curso Profesional de JavaScript

Curso Profesional de JavaScript

Mejora tus habilidades en Javascript. Conoce Typescript y cómo puedes ocuparlo para mejorar el control de tus variables. Comprende conceptos avanzados que te permitan plantear mejores soluciones en tu código. Conoce las APIs del DOM y descubre cómo puedes organizar mejor tu código utilizando patrones de diseño.
Richard B.
Richard B.
richard

2271Puntos

hace 2 años

Todas sus entradas
Escribe tu comentario
+ 2
Ordenar por:
16
554Puntos

Para las personas que no entendieron bien el concepto:

  1. Un ámbito / scope es el entorno en donde una variable / función se define.
  2. Ua variable declara con var no tiene ámbito / scope.
  3. Como un var no tiene un ámbito, var i es movida hacia el inicio de la función printNumbers (hoisting).

Hasta aquí, en tiempo de ejecución tenemos:

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

A esto se le llama hositing, que consiste en mover las declaraciones de variables hacia el top del ámbito actual.


El otro concepto es closures. Un closure es una función dentro de otra. Estas funciones tienen una característica especial: recuerdan el ámbito en donde fueron creadas.

Esto implica que el closure printrecordará el último valor de a, el cual es: 10. Por consiguiente, el resultado de la ejecución será 10 veces 10.


Tip

Nunca uses var 😃

9
15351Puntos

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!

6
16720Puntos

Sigo sin entender por qué se repite el 10 😦

4

Buen trabajo muy bien explicado!

4
253Puntos

Muy interesante, practico y fácil de analizar.

4
1811Puntos

Sutil pero gran diferencia.

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
459Puntos

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

3
15150Puntos

Excelente explicación, corta y directa.

2
29805Puntos

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.

2
7588Puntos

Buen tema no lo tenia en cuenta!!!

1
9426Puntos
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
15898Puntos

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

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.

0
23703Puntos

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

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

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