Una entrevista · 2 preguntas que debería haber clavado
-
¿Qué son los closures?
Dónde me equivoqué: Respondí "una función dentro de otra función". Eso describe una función anidada — básicamente un callback — no un closure. Encima lo planteé como una función que recibe un parámetro, que no tiene nada que ver. Y después me mandé del todo y dije que métodos de array como map, forEach y reduce eran "ejemplos de closures" — no lo son. Esos son funciones de orden superior; el callback que les pasás es un closure solo si captura variables del ámbito que lo rodea. Lo que se me escapó es que un closure se define por retener estado de su ámbito envolvente, no por estar anidado.
La respuesta que debería haber dado
Un closure es una función empaquetada junto con las referencias a su ámbito léxico circundante. El rasgo que lo define no es el anidamiento: es que la función interna conserva acceso a las variables de la función externa incluso después de que esta ya retornó, y puede leer y actualizar ese estado capturado entre llamadas. Una función anidada (o un callback) recién se vuelve un closure cuando efectivamente "cierra sobre" (recuerda) estado de su ámbito envolvente. Ese estado retenido y privado es el punto central. Y los métodos de array como map, forEach o reduce tampoco son closures: son funciones de orden superior (funciones que reciben un callback); el callback es un closure solo cuando busca variables fuera de sí mismo.
function contador() { let cuenta = 0; // capturada por el closure return () => ++cuenta; // recuerda `cuenta` después de que contador() retornó } const siguiente = contador(); siguiente(); // 1 siguiente(); // 2 ← el estado persistió gracias al closureClosuresÁmbito léxicoFunciones de orden superiorJavaScript -
¿JavaScript es de un solo hilo o multihilo?
Dónde me equivoqué: Dije "de un solo hilo" y después traté de respaldarlo con el event loop y me trabé. Llamé al event loop "una cola" y me quedé ahí — es cierto, pero nunca expliqué por qué es una cola, cómo funciona en realidad, ni cómo eso se conecta con que haya un solo hilo. Media respuesta vaga cae peor que un "sí, un solo hilo" dicho con seguridad.
La respuesta que debería haber dado
JavaScript corre sobre un único hilo principal con una sola pila de llamadas, así que ejecuta una cosa a la vez. El event loop es lo que mantiene responsivo a ese único hilo: ejecuta la tarea actual hasta terminarla, mientras que todo lo asíncrono — temporizadores, E/S, fetch, eventos del DOM — se delega al entorno anfitrión (el navegador, o Node vía libuv), que hace ese trabajo por fuera y encola un callback cuando termina. En realidad hay dos niveles: la cola de macrotareas (setTimeout, E/S, eventos de interfaz) y la cola de microtareas (callbacks de promesas, queueMicrotask). El loop recién toma el siguiente callback cuando la pila de llamadas está vacía, y vacía todas las microtareas antes de pasar a la próxima macrotarea. Ese orden — y el hecho de que un bloque síncrono largo congela todo — es exactamente por qué es una cola y no ejecución en paralelo. Para paralelismo real hay que salir del lenguaje: los Web Workers (navegador) y los Worker Threads (Node) corren en hilos del sistema operativo separados y se comunican por paso de mensajes, con SharedArrayBuffer + Atomics para memoria compartida. Entonces: modelo de ejecución de un solo hilo, concurrencia vía event loop, multihilo real vía workers.
console.log('1: síncrono'); // corre ahora, en la pila setTimeout(() => console.log('4: macrotarea'), 0); // encolado como macrotarea Promise.resolve().then(() => console.log('3: microtarea')); console.log('2: síncrono'); // → 1, 2, 3, 4 // la pila se vacía → drena TODAS las microtareas (3) → luego la macrotarea (4)Event loopMicrotareasWeb WorkersConcurrencia