CAP 13 · LEC 01·Conceptos profundos de JavaScript

Hoisting: cómo JavaScript mueve las declaraciones

JavaScript mueve las declaraciones al inicio de su scope antes de ejecutar el código. Entender hoisting explica comportamientos que parecen magia — o bugs misteriosos.

● INTERMEDIO8 min lectura4 ejerciciospor Fernando Herrera · actualizado mayo de 2026
¿Encontraste un error o algo que mejorar?Editá esta lección en GitHub →

¿Qué es el hoisting?

Antes de ejecutar tu código, el motor de JavaScript hace una pasada y mueve las declaraciones al inicio de su scope. No el código físicamente — sino que las registra en memoria primero.

// Esto funciona aunque saludar() está declarada abajo console.log(saludar("Ana")); // "Hola, Ana" function saludar(nombre) { return `Hola, ${nombre}`; } // Así lo ve JavaScript internamente (conceptualmente): // function saludar(nombre) { ... } ← sube completa // console.log(saludar("Ana"));
// TypeScript también aplica hoisting (compila a JS) console.log(saludar("Ana")); // "Hola, Ana" function saludar(nombre: string): string { return `Hola, ${nombre}`; } // Nota: TypeScript puede dar error de análisis estático // en algunos editores, pero el runtime lo permite
SalidaHola, Ana
Hoisting no mueve código

El código fuente no cambia. El motor JavaScript simplemente registra las declaraciones en memoria durante la fase de compilación, antes de la ejecución línea por línea.

Hoisting de var — declaración sube, asignación no

Con var, la declaración sube al inicio del scope de función, pero la asignación se queda en su lugar. El resultado: la variable existe pero vale undefined antes de su asignación.

// Lo que escribes: console.log(ciudad); // undefined (no ReferenceError) var ciudad = "Guadalajara"; console.log(ciudad); // "Guadalajara" // Lo que JavaScript "ve": // var ciudad; ← declaración sube // console.log(ciudad); // undefined // ciudad = "Guadalajara"; ← asignación se queda // console.log(ciudad); // "Guadalajara" function ejemplo() { console.log(x); // undefined var x = 10; console.log(x); // 10 } ejemplo();
// TypeScript permite var pero el comportamiento es igual function ejemplo(): void { console.log(x); // undefined en runtime var x: number = 10; console.log(x); // 10 } ejemplo(); // TypeScript recomienda let/const precisamente // para evitar este comportamiento confuso
Salidaundefined Guerreros undefined 10

Hoisting de funciones — suben completas

Las declaraciones de función (function declarations) son el caso más completo de hoisting: la función entera — nombre y cuerpo — sube al inicio del scope.

// ✅ Declaración de función — hoisting completo console.log(sumar(3, 4)); // 7 function sumar(a, b) { return a + b; } // ❌ Expresión de función — NO hace hoisting del cuerpo try { console.log(restar(10, 3)); // TypeError: restar is not a function } catch (e) { console.log(e.message); } var restar = function(a, b) { return a - b; }; // ❌ Arrow function — mismo comportamiento que expresión try { console.log(multiplicar(2, 5)); } catch (e) { console.log("multiplicar no está lista aún"); } const multiplicar = (a, b) => a * b;
// ✅ Declaración de función — hoisting completo console.log(sumar(3, 4)); // 7 function sumar(a: number, b: number): number { return a + b; } // ❌ TypeScript detecta este error en tiempo de compilación // console.log(restar(10, 3)); // Error: Block-scoped variable used before declaration const restar = (a: number, b: number): number => a - b; // TypeScript protege contra muchos errores de hoisting // gracias al análisis estático
Salida7 restar is not a function multiplicar no está lista aún

let y const — Temporal Dead Zone (TDZ)

let y const también hacen hoisting — pero no se inicializan. Acceder a ellas antes de su declaración lanza un ReferenceError. Ese período se llama Temporal Dead Zone (TDZ).

// ❌ TDZ — ReferenceError try { console.log(nombre); // ReferenceError: Cannot access before initialization let nombre = "Carlos"; } catch (e) { console.log(`Error: ${e.message}`); } // ✅ Uso correcto — declarar antes de usar let contador = 0; contador++; console.log(contador); // 1 // ✅ const también tiene TDZ const PI = 3.14159; console.log(PI); // 3.14159 // Comparación clara: { // TDZ de "letVar" comienza aquí ↑ // console.log(letVar); // ❌ ReferenceError let letVar = "seguro"; // TDZ de "letVar" termina aquí ↓ console.log(letVar); // "seguro" }
// TypeScript detecta el error TDZ en compilación // console.log(nombre); // Error de compilación // let nombre: string = "Carlos"; // ✅ Orden correcto let nombre: string = "Carlos"; console.log(nombre); // "Carlos" // const con tipo explícito const PI: number = 3.14159; console.log(`PI = ${PI}`); // "PI = 3.14159" // En TypeScript, el compilador te avisa antes // de que el runtime lance el error
SalidaError: Cannot access 'nombre' before initialization 1 3.14159 seguro
TipoHoistingValor antes de declaraciónRe-asignable
varSí — declaración subeundefined (silencioso)
letSí — pero en TDZReferenceError (explícito)
constSí — pero en TDZReferenceError (explícito)No
function declarationSí — completaFunción disponible
TDZ es una mejora, no un problema

El error de TDZ con let/const es más útil que el undefined silencioso de var. Te dice exactamente qué salió mal en lugar de dejar que el bug se propague silenciosamente.

Practica