En el post de variables, tipos de dato, scope y hoisting vimos los conceptos. Ahora vamos a practicar como de verdad lo evalúan en las entrevistas: "¿Qué imprime este código?"
Cómo usar este post: lee cada ejercicio, anota tu respuesta y después abre la solución. Si quieres comprobarlo, córrelos en la consola de tu navegador. Todas las respuestas están probadas en Node.js.
Repaso rápido
| Declaración | ¿Hace hoisting? | Antes de su línea |
|---|---|---|
var | Sí | undefined |
let / const | Sí, pero sin inicializar | ReferenceError (temporal dead zone) |
Declaración de function | Sí, completa | Se puede llamar |
| Expresión de función / arrow | Solo la variable | Depende de si es var, let o const |
class | Sí, pero sin inicializar | ReferenceError |
Y recuerda: el código no se mueve. Antes de ejecutar un scope, el motor registra sus declaraciones. Lo que cambia es si se inicializan o no.
Ejercicio 1: para calentar
console.log(saludo); var saludo = "Hola"; console.log(saludo); undefined y luego "Hola". saludo se registra antes de que corra el código y se inicializa como undefined. La asignación = "Hola" pasa en la línea 2, así que el primer log ve undefined.
Ejercicio 2: let y const
console.log(primera); console.log(segunda); let primera = "Soy un let"; const segunda = "Soy un const"; Un solo error:
ReferenceError: Cannot access 'primera' before initialization. La segunda línea nunca se ejecuta, porque el primer error detiene la ejecución.
Y lo más importante: primera y segunda sí hacen hoisting. Si no lo hicieran, el error sería "primera is not defined". "Cannot access before initialization" significa que el motor sabe que la variable existe, pero todavía está en la temporal dead zone.
Ejercicio 3: tres tipos de funciones
console.log(suma(2, 3)); function suma(a, b) { return a + b; } console.log(suma2(2, 3)); const suma2 = function (a, b) { return a + b; }; 5 y luego ReferenceError: Cannot access 'suma2' before initialization. Las declaraciones de función hacen hoisting con todo y su cuerpo, así que se pueden llamar antes. suma2 es solo una variable const que guarda una función, así que sigue las reglas de const. Lo mismo pasa con las arrow functions.
Ejercicio 4: la misma función, pero con var
despedir(); var despedir = function () { console.log("Adiós"); }; TypeError: despedir is not a function. Esta vez no es un ReferenceError. Con var, despedir existe y vale undefined, y llamar a undefined() es un TypeError. A los entrevistadores les encanta esta diferencia.
Ejercicio 5: el bloque que se escapa
if (true) { var escapa = "var"; let seQueda = "let"; } console.log(escapa); console.log(seQueda); "var" y luego ReferenceError: seQueda is not defined. var ignora el scope de bloque; pertenece a la función que lo contiene (o al scope global). let solo existe dentro de las { }.
Ejercicio 6: shadowing con var
var x = 1; function prueba() { console.log(x); var x = 2; } prueba(); undefined. ¡No 1! Dentro de prueba hay un var x local, que se registra al inicio de la función y se inicializa como undefined. Esa variable tapa (shadowing) a la x global, así que el log ve la local antes de su asignación.
Ejercicio 7: shadowing con let
let x = 1; { console.log(x); let x = 2; } ReferenceError: Cannot access 'x' before initialization. Podrías esperar 1, del scope de afuera. Pero el bloque interno tiene su propio let x, que hace hoisting al inicio del bloque y queda atorado en la temporal dead zone. La x interna tapa a la externa desde la primera línea del bloque.
Esta es la mejor prueba de que let sí hace hoisting.
Ejercicio 8: sin declaración adentro
var a = 1; function externa() { console.log(a); a = 2; } externa(); console.log(a); 1 y luego 2. Dentro de externa no hay var, let ni const, así que a se refiere a la variable de afuera por la scope chain. La función la lee y luego la reasigna.
Ejercicio 9: el mismo nombre, dos declaraciones
console.log(typeof foo); var foo = 1; function foo() {} console.log(typeof foo); "function" y luego "number". Las dos declaraciones se registran, y en el hoisting gana la declaración de función. Después corre la línea foo = 1 y la reemplaza por un número. (Por favor, nunca escribas esto en código real.)
Ejercicio 10: typeof y la TDZ
console.log(typeof noDeclarada); console.log(typeof enTDZ); let enTDZ = "hola"; "undefined" y luego ReferenceError: Cannot access 'enTDZ' before initialization. Normalmente typeof es "seguro": con una variable que no existe para nada, regresa "undefined" en lugar de lanzar un error. Pero una variable en la temporal dead zone es diferente. Sí existe, solo que no está inicializada, y typeof no te salva.
Ronda extra
Dos rápidos que salen en entrevistas más avanzadas:
new Usuario(); class Usuario {} function saludar(a = b, b = "hola") { return a; } saludar(); Los dos lanzan un
ReferenceError. - Las clases hacen hoisting como
let: están en la temporal dead zone hasta su declaración. - Los parámetros por defecto se evalúan de izquierda a derecha. Cuando
aintenta leerb,btodavía no se ha inicializado.
¿Cómo te fue?
- De 10 a 12: estás lista o listo para esta parte de la entrevista.
- De 6 a 9: repasa la temporal dead zone y el shadowing (ejercicios 6, 7 y 10).
- Menos de 6: regresa al post de conceptos y vuelve a intentarlo en un par de días.
¿Cuál te hizo caer? Cuéntame en los comentarios 👇