63

La diferencia entre let y var en Javascript

2165Puntos

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

2165Puntos

hace 2 años

Todas sus entradas
Escribe tu comentario
+ 2
Ordenar por:
14
552Puntos

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

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!

4
15177Puntos

Sigo sin entender por qué se repite el 10 😦

4
1787Puntos

Sutil pero gran diferencia.

4
253Puntos

Muy interesante, practico y fácil de analizar.

4

Buen trabajo muy bien explicado!

3
14236Puntos

Excelente explicación, corta y directa.

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

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

2
7476Puntos

Buen tema no lo tenia en cuenta!!!

2
26348Puntos

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.

1
11298Puntos

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

0
16086Puntos

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

0

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