Javascript

Hoisting en JavaScript

En este tutorial te explico qué es el hoisting y cómo funciona este mecanismo en JavaScript.

En JavaScript, el hoisting es un mecanismo por el cual las declaraciones de variables y funciones son procesadas y trasladadas a la parte superior del scope antes de la ejecución del código.

La traducción de Hoisting en español podría ser levantamiento o alzamiento, haciendo de alguna forma referencia a este “movimiento” de las declaraciones que sucede en fase de compilación.

Este mecanismo permite encontrar en JavaScript excentricidades como la siguiente:

saludar('Ariel'); // devuelve 'Hola, mi nombre es Ariel' en consola

function saludar (nombre) {
  console.log('Hola, mi nombre es ' + nombre);
}

En este ejemplo, la función saludar es invocada antes de haber sido declarada. Sin embargo, la misma será ejecutada exactamente igual que si hubiera sido invocada luego de su declaración.

¿Qué sucede si en lugar de declarar una función como sentencia, como en el ejemplo anterior, la creamos con una expresión de función?

dormir(); // TypeError: dormir is not a function

var dormir = function () {
  console.log('ZZZzzz');
}

El hoisting no opera sobre las expresiones de función, y por lo tanto el resultado es un error de tipo TypeError que nos alerta que lo que intentamos invocar no es una función.

Veamos otro ejemplo con funciones:

function iniciar () {
  variable1 = 10;
  var variable2 = 20;
}

iniciar();

Ahora, veamos qué sucede si intento mostrar inmediatamente después el contenido de las variables en la consola:

console.log(variable1); // devuelve 10

console.log(variable2); // devuelve error de tipo "ReferenceError"

Esto sucede porque en realidad el código anterior es interpretado en tiempo de ejecución de la siguiente forma:

var variable1;

function iniciar () {
  variable1 = 10;
  var variable2 = 20;
}

iniciar();

Por lo cual, variable1 se encuentra en el scope global, y por lo tanto existe a la hora de hacer console.log, mientras que variable2 no existe fuera del scope de la función iniciar, y por lo tanto al no estar declarada en ese contexto, intentar loguearla devuelve un error de tipo ReferenceError: variable2 is not defined.

Algo similar sucede en el ejemplo a continuación:

console.log(ejemploDeHoisting); // devuelve undefined

var ejemploDeHoisting = 'Esta variable fue hoisteada';

console.log(ejemploDeHoisting); // "Esta variable fue hoisteada"

Aquí el console.log devuelve undefined, y esto tendrá sentido si comprendemos que en realidad este código es interpretado de la siguiente forma:

var ejemploDeHoisting;

console.log(ejemploDeHoisting); // undefined

ejemploDeHoisting = 'Esta variable fue hoisteada';

console.log(ejemploDeHoisting); // "Esta variable fue hoisteada"

Esto es consecuencia de que JavaScript realiza el hoisting solo en la declaración, y no en la iniciación.

En este ejemplo, luego de que el mecanismo de Hoisting actúa reposicionando la definición de la variable ejemploDeHoisting, la misma pierde su inicialización y queda sin un valor. Y cuando una variable es definida sin inicializar con un valor, JavaScript le asigna automáticamente el tipo y valor undefined. Entonces, al llegar a la fase de ejecución, la interpretación final del código equivale a:

var ejemploDeHoisting = undefined;

console.log(ejemploDeHoisting); // undefined

ejemploDeHoisting = 'Esta variable fue hoisteada';

console.log(ejemploDeHoisting); // "Esta variable fue hoisteada"

Es importante aclarar que este comportamiento es propio de las variables declaradas con var. Si utilizamos let o const las variables darán un error ReferenceError al intentar accederlas antes de ser declaradas.

Como conclusión, es recomendable siempre declarar las variables en JavaScript, de forma explícita, utilizando var, const o let, según corresponda. Esto explicita al interprete con exactitud cómo debe manejarlas en fase de ejecución, además de dar claridad a la intención del programador sobre el contexto de las mismas.

Si aún no lo leíste, te recomiendo mi post sobre variables:

Variables en JavaScript: var, const y let