Mientras vamos progresando en nuestra carrera profesional como programadores de JavaScript, vamos aprendiendo sobre muchos temas y conceptos sobre las herramientas y técnicas que usamos para crear productos o apps. Una de las cosas que me he dado cuenta es que si este conocimiento no es sólido, es más difícil resolver errores.
Ciertamente, aprender sobre los orígenes de cualquier herramienta que utilizamos no nos limita a la hora de utilizarlas, pero sí que nos ayuda a poder utilizarlas de la manera más correcta, a resolver cualquier error que nos salga por el camino y lo más importante, evitar futuros errores.
Me gusta comparar var, let y const martillos. No necesitas leerte un manual de la historia de un martillo para empezar a usarlo, pero si que es util saber Qué tipo de martillo funciona mejor para según la situación, o corres el riesgo de lastimarte un dedo por el camino 😅
La importancia de la encapsulación
Un programa en donde todas las variables son globales o accesibles en cualquier sitio está mal visto, y con mucha razón. Si dependemos de variables globales en nuestro código, es más fácil sobre escribirlas y hace muy complejo el mantenimiento del mismo. No hay nada de malo en las variables globales, pero si el depender de ellas para todo, son útiles en algunos casos concretos.
Antes de que se introdujeran los tipos de variables let
y const
, usábamos nuestro viejo amigo var
. Las variables de tipo var
son accesibles en el ámbito de función en el que fueron creadas, o cualquier otro ámbito hijo y la única manera que teníamos en JavaScript para poder encapsular variables era usando funciones.
Una de las formas más usadas y habituales era creando funciones que se ejecutaban a sí mismas, o IIFEs (Immediately Invoked Function Expression)
En el ejemplo anterior, contador
es asignado a lo que devuelve la función soyUnaIIFE
, que en este caso es un objeto con dos funciones. Como la variable miValor
está creada dentro de soyUnaIIFE
, no puede ser accesible en ningún otro sitio.
Se entiende porqué las funciones forman un ámbito. Pero, por qué necesitamos más granularidad para definir ámbitos?
Entre menos exposición, más seguro
Al limitar la exposición de variables al ámbito en el cual tienen que ser usadas, reducimos los errores y problemas que puede ocacionar, como por ejemplo colisión de nombres, dependencias innecesarias, uso inesperado de variables por terceros.
Un caso concreto en el que el ámbito de funciones no es suficiente, es cuando asignamos eventos dentro de un for loop:
Lo que esperaríamos es que al clicar en cada uno de los botones, es ver en la consola el valor de i
en el momento que asignamos el listener, pero vemos que no es así. Vemos el valor de i
después de recorrer todo el loop.
Con la llegada de ES2015, se introdujeron los nuevos tipos de variable let
y const
, que su ámbito léxico no se limita a las funciones, sino a los bloques. Un bloque es cualquier parte de nuestro programa que que esté delimitada por los corchetes ({ }
). Esto nos ayuda a resolver el problema del código anterior, simplemente cambiando el tipo de variable de i
a let
:
Podría seguir escribiendo sobre let y const y sobre su creación, pero no me parece demasiado práctico ni efectivo para el tiempo que le estás dedicando a leer este artículo, así que vamos al grano.
let
yconst
sólo se pueden declarar una sola vezconst
No se puede reasignar (pero no es una constante)let
yconst
NO se pueden usar antes de ser declaradas (Temporal Dead Zone)let
yconst
también son elevadas
1. let
y const
sólo se pueden declarar una sola vez
let
y const
solo permiten ser declaradas una sola vez en su ámbito léxico. A diferencia de nuestro viejo amigo var
, que es más permisivo.
Cuando introducimos diferentes ámbitos de bloque, tanto let
como const
se tratan como variables distintas
Como el ámbito de let
y const
se limitan a los bloques, cada vez que dentro de un bloque declaramos una variable cualquiera de estos tipos, creamos una nueva, no reutilizamos otra de un ámbito superior, como pasa con var
. Esto es lo que se le llama Shadowing. También pasa con var
La razón por la que no se pueden re-declarar es para evitar problemas en los que otras partes (tanto internas como externas) de tus apps puedan ocasionar al cambiar directa o indirectamente cualquier variable que estes usando. No queremos que alguien pueda cambiar el tipo de una variable cuando esperamos otro...
2. const
No se puede reasignar (pero no es una constante)
Su nombre puede ser malinterpretado, pero const
no se refiere a que estamos declarando una constante, sino que el valor en memoria al que esta variable apunta no se puede cambiar dentro del ciclo de la misma. Por si guardamos un objeto o un arreglo en una variable de tipo const, es permitido modificarlo pero no reasignarlo:
Una de las razones principales que verás por las que muchos developers usan const
para todo es porque al usarla, mostramos la intención de no cambiarla a otros developers que vean tu código. Puede parecer muy razonable esta razón, pero creo firmemente que no está fundamentada en el uso correcto de la misma. Veamos el siguiente ejemplo:
La variable nombre
, independientemente de pueda o no ser cambiada, en el unico sitio en el que podría ser cambiada es entre const nombre = user.name
y return nombre
(que es literalmente media línea). En este caso, es seguro usar tanto let como var (porque ambos ámbitos léxicos son pequeños)
3. let
y const
NO se pueden usar antes de ser declaradas (Temporal Dead Zone)
Seguro has escuchado hablar del Temporal Dead Zone, y todo es culpa de const
. Como const
no puede ser re-asignada, tanto la declaración como la asignación tiene que pasar al mismo tiempo. Si con const
pasara lo mismo que pasa con var
en el proceso de compilación (las variables y declaraciones de funciones son "elevadas" o Hoisting), o se cumpliría la regla que no se puede re-asignar, ya que el primer valor que tendría sería undefined
. let
sufre de lo mismo porque, bueno, al ser introducidas al mismo tiempo, tenía más sentido (Obviamente no es exactamente esto, pero estor parafraseando un poco para no hacer este post aburrido 😅)
4. let
y const
también son elevadas
Puede que por tener ámbitos más "pequeños" (bloques) se tenga la falsa idea que no son elevadas como var
, pero si que lo son. es importante tener esto en cuenta para cuando combinamos multiples ámbitos y tenemos ámbitos hijos.
Desafío!
Ahora con las reglas refrescadas, veamos un simple ejemplo para poder solidificar conceptos:
¿Qué debes modificar en esta función para que en la consola te salgan todos los números del arreglo values?
Conclusión
Es importante saber qué tipo de herramientas tenemos cuando estamos creando nuestros programas. De esta manera evitamos usar herramientas erróneas en situaciones erróneas. Así como existen diferentes tipos de martillos para diferentes tipos de acciones, var
, let
y const
son tres herramientas diferentes, que ninguna reemplaza a la otra, que debemos saber su función y cómo usarlas.
Sé que algunas de las cosas que viste en el post pueden ser algo controversiales para la manera habitual en la que se usan o enseñan, mi objetivo es tratar de explicar los fundamentos para que puedas crear TU criterio y poder tomar decisiones mejor formadas en el futuro.
A programar!