El hoisting es uno de los comportamientos de JavaScript que más confunde a los desarrolladores que vienen de otros lenguajes. El motor de JavaScript no ejecuta el código de arriba a abajo de forma lineal: antes de ejecutar, procesa las declaraciones y las mueve conceptualmente al inicio del scope. Entenderlo evita una categoría entera de bugs.
Qué se eleva y qué no
JavaScript distingue entre declaración e inicialización. El hoisting afecta solo a las declaraciones. Los valores asignados permanecen donde están en el código:
// Lo que escribes: console.log(nombre); // ¿qué pasa aquí? var nombre = 'Ana'; console.log(nombre); // Lo que el motor interpreta: var nombre; // declaración elevada, valor: undefined console.log(nombre); // undefined nombre = 'Ana'; // inicialización en su lugar original console.log(nombre); // 'Ana'
Function declarations se elevan completas
Las funciones declaradas con function nombreFuncion() se elevan enteras, incluyendo su cuerpo. Por eso puedes llamar a una función antes de declararla en el código:
saludar('Luis'); // Funciona: imprime "Hola, Luis"
function saludar(nombre) {
console.log('Hola, ' + nombre);
}
// Las function expressions NO se elevan igual:
despedir('Ana'); // TypeError: despedir is not a function
var despedir = function(nombre) {
console.log('Adiós, ' + nombre);
};
Con var despedir, la declaración se eleva como undefined. Intentar llamar a undefined lanza TypeError. Con let o const, lanzaría ReferenceError por la Temporal Dead Zone.
La Temporal Dead Zone de let y const
let y const también se elevan, pero el motor no las inicializa. Desde el inicio del bloque hasta la línea de declaración existe una zona prohibida llamada Temporal Dead Zone (TDZ). Acceder a la variable en ese intervalo lanza ReferenceError:
{
// Inicio del bloque: TDZ empieza aquí para 'x'
console.log(typeof x); // ReferenceError (no 'undefined' como con var)
console.log(x); // ReferenceError
let x = 5; // TDZ termina aquí
console.log(x); // 5
}
La TDZ existe por diseño: detecta errores de orden que con var pasarían silenciosamente como undefined. Este comportamiento explica por qué typeof no es seguro con let y const como lo era con var.
Hoisting en clases
Las clases se comportan como let: se elevan pero no se inicializan, quedando en TDZ:
const obj = new MiClase(); // ReferenceError
class MiClase {
constructor() {
this.valor = 42;
}
}
// Correcto:
class Persona {
constructor(nombre) {
this.nombre = nombre;
}
}
const p = new Persona('María'); // OK
El orden importa: un ejemplo real
El hoisting puede ocultar bugs cuando hay funciones con el mismo nombre. La última declaración de función gana, independientemente del orden en el código:
console.log(calcular(5)); // 25, no 10
function calcular(x) {
return x * 2; // Esta queda sobreescrita
}
function calcular(x) {
return x * x; // Esta es la que prevalece (última declarada)
}
La regla práctica: declara las variables y funciones antes de usarlas, aunque JavaScript te permita no hacerlo. El código que depende del hoisting para funcionar es más difícil de mantener y revisar.
