Escuela Frontend
JavaScript

Entiende el Hoisting en JavaScript con Ejemplos Prácticos

Claudia Valdivieso
Autora
Claudia Valdivieso

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 y let).
  • Qué es la Temporal Dead Zone.

Ejemplos

Considera este ejemplo:

var source = "escuelafrontend.com";
console.log(source);
function print() {
console.log("desde print: " + source);
}
print();

Cuya salida es:

escuelafrontend.com
desde print: escuelafrontend.com

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 a escuelafrontend.com .
  • Se encuentra la línea de código console.log y el valor de source 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ón print(). Esto resulta en las declaraciones dentro de la función que se ejecuta y la cadena desde 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:

console.log(source);
print();
function print() {
// definición de la función
console.log("desde print: " + source);
}
var source = "escuelafrontend.com"; // declaración de la variable y asignación de su valor
print();

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.

...

...

...

...

...

...

...

...

...

...

Resultado:

undefined
desde print: undefined
desde print: escuelafrontend.com

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:

Encuentra la variable source y la carga en la memoria. Como el código aún no se ha ejecutado su valor es undefined.

La función printMe también se carga en memoria.

Para comprobar si has entendido bien el concepto de hoisting y contexto de ejecución, veamos otro ejemplo:

source = 5;
console.log(source);
printFnExp();
// sintaxis de la expresión de función
var printFnExp = function print() {
console.log("desde la función: " + source);
};
var source;

Salida:

5
Uncaught TypeError: printFnExp is not a function

¿Te causa sorpresa el error en la salida? No debería.

Para entenderlo mejor, echa un vistazo a los siguientes diagramas.

  • Las variables source y printFnExp 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:

source = 5;
console.log(source);
// sintaxis de la expresión de función
var printFnExp = function print() {
console.log("desde la función: " + source);
};
printFnExp();
var source;

Salida:

5
desde la función: 5

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:

console.log(source);
print();
function print() {
console.log("desde print: " + source);
}
let source = "escuelafrontend.com";

Salida:

Uncaught ReferenceError: source is not defined

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.

// Temporal Dead Zone
///////////////////////////////////////////////
console.log(source); ///
print(); ///
///
function print() { ///
console.log("desde print: " + source); ///
} ///
///
///////////////////////////////////////////////
let source = "escuelafrontend.com"; // Final de la Temporal Dead Zone

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:

let source = "escuelafrontend.com";
console.log(source);
function print() {
console.log("desde print: " + source);
}
print();

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:

let source; // declaración
console.log(source);
source = "escuelafrontend.com"; // inicialización
function print() {
console.log("desde print: " + source);
}
print();

Salida:

undefined
from print: escuelafrontend.com

Ejemplo con const:

const source;
console.log(source)

Salida:

Uncaught SyntaxError: Missing initializer in const declaration

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:

const source = "escuelafrontend.com";
console.log(source);

Salida:

escuelafrontend.com

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

Artículos Relacionados

¿Quieres mejorar tus habilidades de frontend?