Hoisting en JavaScript: variables y funciones elevadas al inicio del scope

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.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP