Hoisting es un concepto fundamental del lenguaje JavaScript debido a que te ayuda a entender cómo tu código es ejecutado y por ello, es uno de los temas que se preguntan con frecuencia en las entrevistas (en serio) .
Aunque el concepto es muy sencillo, la mayoría de las veces se malinterpreta. En este artículo vas a aprender qué es hoisting con la ayuda de algunos ejemplos.
Lo que vas a aprender
Después de leer este artículo vas entender:
- El contexto de ejecución y sus fases.
- Cómo funciona el hoisting con variables (
var
) y funciones (function
) - Casos especiales de hoisting (
const
ylet
). - Qué es la Temporal Dead Zone.
Ejemplos
Considera este ejemplo:
Cuya salida es:
Es bastante sencillo, ¿verdad?
El código se ejecuta línea por línea.
Echemos un vistazo a lo que sucede:
- Se crea la variable
source
y se le asigna un valor aescuelafrontend.com
. - Se encuentra la línea de código
console.log
y el valor desource
se imprime en la consola:escuelafrontend.com
. - Del mismo modo, la siguiente línea es la definición de la función
print
. Seguido de la llamada real a la funciónprint()
. Esto resulta en las declaraciones dentro de la función que se ejecuta y la cadenadesde print: escuelafrontend.com
se imprime en la consola.
Pero, ¿es realmente tan sencilla la ejecución de código en JavaScript?
Echa un vistazo a esta variación del mismo ejemplo:
Aquí estamos llamando la variable source
en la consola y la función print
antes de que sean declaradas.
¿Crees que este código arrojará un error?
¿Cuál crees que es la salida esperada para este ejemplo?
Tómate un momento para pensar antes de ver la respuesta más abajo.
.
.
.
.
.
.
.
.
En la mayoría de los lenguajes de programación esto lanzaría una excepción.
Pero bueno, JavaScript lo permite. ¿Cómo? Debido al hoisting.
El concepto más popular de hoisting es:
Hoisting en JavaScript te permite acceder a funciones y variables antes de que hayan sido creadas.
Este concepto no es tan preciso, así que para entender cómo funciona el hoisting necesitamos entender el contexto de ejecución.
Contexto de Ejecución
El contexto de ejecución es el entorno que prepara el motor de JavaScript para ejecutar el código que escribimos.
En pocas palabras, es la información necesaria para ejecutar el código.
El contexto de ejecución se crea en dos fases:
Fase de Creación
- El código es escaneado/parseado para las variables y funciones.
- Se reserva espacio para las variables en memoria.
- Las definiciones de las funciones se cargan en la memoria.
Fase de Ejecución
- El código se ejecuta línea por línea con la información de la fase de creación.
- Se asignan valores a las variables.
- Se ejecutan las funciones.
Veamos estas fases para el ejemplo ilustrado anteriormente:
Para comprobar si has entendido bien el concepto de hoisting y contexto de ejecución, veamos otro ejemplo:
Salida:
¿Te causa sorpresa el error en la salida? No debería.
Para entenderlo mejor, echa un vistazo a los siguientes diagramas.
- Las variables
source
yprintFnExp
se cargan en memoria. - La función
print
se carga en la memoria.
Fíjate bien en printFnExp
: es una expresión de función. Esto indica que es una variable cuyo valor apunta a una función.
En términos simples, la función está asignada a una variable. Para llamar a la función que está asignada a una variable, tenemos que escribir “nombreDeLaFuncion” seguido de paréntesis. Por ejemplo: printFnExp()
- El valor 5 se asigna a
source
. - 5 se imprime en la consola.
- Se llama a
printFnExp
. Sin embargo, arroja un error -Uncaught TypeError: printFnExp no es una función
.
Esto sucede porque la variable fue “hoisteada”, pero su valor inicial sigue siendo undefined
. Por lo tanto, obtenemos un error por intentar llamar a una función sobre un valor indefinido.
La sentencia que asigna la referencia de la función print
a printFnExp
no se ha ejecutado.`
Para solucionar este error, vea los cambios en el código que aparecen a continuación:
Salida:
Aquí se ha asignado a printFnExp
la referencia de la función print
. Por lo tanto, ahora es posible invocar la expresión de la función así - printFnExp();
Nota: Hay más en el contexto de ejecución que lo mencionado en este artículo. He cubierto sólo lo suficiente para que se entienda el concepto de hoisting.
Casos Excepcionales
El hoisting funciona de forma diferente si la declaración de la variable se realiza mediante let
o const
.
En el caso de var
el valor se inicializa a indefinido durante la fase de creación. Sin embargo, en el caso de let
y const
el valor sólo se inicializa durante la fase de ejecución.
Mira el ejemplo siguiente:
Salida:
Como el valor de source
no se inicializa durante la fase de creación, source
no tiene ninguna referencia al valor en memoria. Debido a esto, se lanza un error de referencia para la declaración console.log(source);
Este concepto también se conoce como Temporal Dead Zone (zona muerta temporal). Significa que no se puede acceder a una variable hasta que se declare.
Veamos el mismo ejemplo con estos conocimientos.
Arriba estamos representando la Temporal Dead Zone para la variable source
. Obtendremos un error de referencia si intentamos acceder a la fuente dentro de este bloque.
A continuación se muestra el uso correcto de let
:
Durante la fase de ejecución, si no se proporciona ningún valor junto con la declaración, el valor se considera undefined
.
Mira el ejemplo:
Salida:
Ejemplo con const
:
Salida:
Una const
indica un valor constante. Este valor no puede ser cambiado durante la ejecución del código. Por lo tanto, tiene sentido que requiera un valor inicializador en el momento de la declaración.
Uso correcto de const
:
Salida:
Recapitulación y Conclusión
En resumen, el mejor concepto de hoisting sería
Hoisting es cuando las funciones y las variables se almacenan en memoria para un contexto de ejecución antes de ejecutar nuestro código.
Las funciones se almacenan con una referencia a las funciones completas, las variables declaradas con var
con el valor de undefined
, y las variables declaradas con let
y const
se almacenan sin inicializar.
Las mejores prácticas sugieren declarar las variables al principio del bloque de código. También es preferible utilizar let
y const
para la declaración de variables. Esto mejora la legibilidad del código.
Aunque el uso de la declaración de variables mediante var
se ha convertido en algo obsoleto, es importante conocer este concepto, ya que es posible que te lo encuentres en algunas de las bases de código existentes o incluso en una entrevista donde tengas que refactorizar dicho código con las últimas características de JavaScript.
El conocimiento del hoisting te ayudará a evitar cualquier error y confusión relacionados con la declaración de variables y su uso.
Otros Recursos
- JavaScript: The Hard Parts, v2
- Hoisting - Wes Bos
- 🔥🕺🏼 JavaScript Visualized: Hoisting
- Hoisting - MDN, para tenerla en cuenta aunque no es muy clara la explicación.