Comprende el modelo del lenguaje, predice su comportamiento y aprende a responder con precisión técnica y experiencia práctica.
10 capítulos181 minJunior → Senior
Progreso del módulo0%
0 de 10 dominados
Cómo estudiar: lee primero la definición y la respuesta Senior. Después intenta explicar el ejemplo sin verlo. Finalmente cambia el estado a “Dominado” solo cuando puedas responder los follow-ups.
★★★★★Junior → Senior🔥 Sale muchísimo
Variables: var, let y const
Una variable vincula un nombre con un valor. La diferencia importante entre var, let y const no es solo si permiten reasignación: cambia su alcance, su inicialización, su relación con el objeto global y la forma en que ayudan a prevenir errores.
12 min
🟢 Repaso rápido🟡 Deep dive🔴 Senior notes
¿Qué es y por qué existe?
Una variable vincula un nombre con un valor. La diferencia importante entre var, let y const no es solo si permiten reasignación: cambia su alcance, su inicialización, su relación con el objeto global y la forma en que ayudan a prevenir errores.
Cómo funciona internamente
Antes de ejecutar el código, el motor crea bindings dentro de un entorno léxico. var se inicializa con undefined y tiene alcance de función; let y const existen desde el inicio del bloque, pero permanecen sin inicializar dentro de la Temporal Dead Zone hasta llegar a su declaración. const impide reasignar el binding, no volver inmutable al valor apuntado.
Puntos importantes
Usa const por defecto y let cuando realmente necesitas reasignar.
Evita var en código moderno: su alcance de función y hoisting facilitan errores.
const protege la referencia, no las propiedades internas de objetos o arreglos.
Una redeclaración con var puede pasar silenciosamente; con let y const produce error.
El binding user sigue apuntando al mismo objeto; solo cambió una propiedad. attempts sí necesitaba un nuevo valor, por eso let es adecuado. Este criterio comunica intención al lector y reduce estados accidentales.
Errores comunes
Decir que const vuelve inmutable un objeto.
Usar let en todas partes por costumbre.
Confundir redeclaración con reasignación.
Pensar que let no tiene hoisting; sí lo tiene, pero permanece en TDZ.
Pregunta típica de entrevista
¿Cuál es la diferencia real entre var, let y const?
Respuesta Junior
var tiene alcance de función y let/const alcance de bloque. const no se puede reasignar.
Respuesta Mid
Además, var se inicializa como undefined durante el hoisting, mientras let y const permanecen en TDZ. const evita reasignar el binding, pero no hace inmutable al objeto.
Respuesta Senior
La elección comunica intención y afecta mantenibilidad. En código moderno usaría const por defecto, let para reasignaciones justificadas y evitaría var. También distinguiría binding de valor: const fija la referencia, no la estructura interna. En módulos, una declaración de nivel superior tampoco se comporta igual que una propiedad global creada por var en scripts clásicos.
Follow-up frecuente: ¿Por qué let y const también están “hoisted” si usarlas antes de declararlas lanza ReferenceError?
El entrevistador quiere saber…
No busca que recites tres definiciones; quiere comprobar si entiendes bindings, alcance, inicialización y mutabilidad.
El scope determina desde dónde puede resolverse un identificador. En JavaScript esa resolución es léxica: depende de dónde fue escrito el código, no de dónde se invocó una función.
15 min
🟢 Repaso rápido🟡 Deep dive🔴 Senior notes
¿Qué es y por qué existe?
El scope determina desde dónde puede resolverse un identificador. En JavaScript esa resolución es léxica: depende de dónde fue escrito el código, no de dónde se invocó una función.
Cómo funciona internamente
Cada ejecución crea contextos y entornos léxicos con un registro de bindings y una referencia al entorno exterior. Cuando se usa un nombre, el motor busca primero en el entorno actual y después recorre la cadena exterior hasta encontrarlo o lanzar ReferenceError.
Puntos importantes
Existen scope global, de módulo, de función y de bloque.
La cadena de scopes se define al crear la función.
Un parámetro pertenece al scope de la función.
Shadowing es válido, pero puede reducir claridad.
Ejemplo
const role = "global";
function createMessage() {
const role = "admin";
return function show() {
console.log(role);
};
}
const message = createMessage();
message();
Resultadoadmin
Qué está pasando
show fue definida dentro de createMessage, por eso su entorno exterior es el scope donde role vale admin. El lugar desde el que luego se llama message no cambia esa relación léxica.
Errores comunes
Confundir scope léxico con el valor dinámico de this.
Pensar que una función busca variables desde donde se llama.
Usar demasiados nombres sombreados.
Confundir una variable inexistente con una variable cuyo valor es undefined.
Pregunta típica de entrevista
¿Qué significa que JavaScript tenga lexical scope?
Respuesta Junior
Que una función puede acceder a variables de su scope y de los scopes exteriores.
Respuesta Mid
La cadena de resolución depende de la posición donde la función fue declarada, no del lugar desde el que se invoca.
Respuesta Senior
Cada función conserva una referencia a su entorno léxico exterior. Esa estructura permite closures y hace predecible la resolución de identificadores. No debe confundirse con this, cuyo valor depende de la forma de invocación salvo en arrow functions.
Follow-up frecuente: ¿Qué diferencia hay entre scope chain y prototype chain?
El entrevistador quiere saber…
La pregunta suele preparar el terreno para closures, hoisting y shadowing.
Hoisting es la forma observable de la fase de creación del contexto de ejecución: las declaraciones se registran antes de ejecutar las sentencias. No significa que el motor “mueva” físicamente líneas.
14 min
🟢 Repaso rápido🟡 Deep dive🔴 Senior notes
¿Qué es y por qué existe?
Hoisting es la forma observable de la fase de creación del contexto de ejecución: las declaraciones se registran antes de ejecutar las sentencias. No significa que el motor “mueva” físicamente líneas.
Cómo funciona internamente
Las function declarations quedan disponibles con su cuerpo. var se crea e inicializa con undefined. let, const y class se crean pero no se inicializan hasta evaluar su declaración; el intervalo anterior es la TDZ.
Puntos importantes
Las function expressions siguen las reglas de la variable que las contiene.
typeof sobre una variable no declarada devuelve "undefined", pero sobre un let en TDZ lanza ReferenceError.
La TDZ evita leer valores antes de que su inicialización sea explícita.
Las clases también tienen TDZ.
Ejemplo
console.log(total); // undefined
var total = 3;
try {
console.log(price);
} catch (error) {
console.log(error.name);
}
let price = 10;
Resultadoundefined
ReferenceError
Qué está pasando
Durante la creación, total ya existe y vale undefined. price también existe, pero todavía no está inicializada, así que acceder a ella viola la TDZ.
Olvidar que una function expression no se comporta como function declaration.
Pregunta típica de entrevista
¿Qué imprime un acceso a var y a let antes de declararlas?
Respuesta Junior
var imprime undefined; let lanza ReferenceError.
Respuesta Mid
Ambas declaraciones se registran antes de ejecutar. La diferencia es que var se inicializa con undefined, mientras let permanece sin inicializar en TDZ.
Respuesta Senior
Lo explicaría mediante las fases de creación y ejecución del contexto, no como movimiento textual. También distinguiría declarations de expressions y mencionaría que class comparte la semántica de TDZ.
Follow-up frecuente: ¿Qué ocurre con una función declarada dentro de un bloque y por qué conviene evitar depender de diferencias históricas entre modos?
El entrevistador quiere saber…
El entrevistador evalúa si conoces el modelo de ejecución, no solo si memorizaste una salida.
Un closure es la combinación de una función y la referencia a su entorno léxico. Permite que la función siga accediendo a bindings externos incluso después de que la ejecución exterior terminó.
18 min
🟢 Repaso rápido🟡 Deep dive🔴 Senior notes
¿Qué es y por qué existe?
Un closure es la combinación de una función y la referencia a su entorno léxico. Permite que la función siga accediendo a bindings externos incluso después de que la ejecución exterior terminó.
Cómo funciona internamente
Cuando una función interna escapa —por retorno, callback o listener— el motor conserva los bindings que todavía pueden ser alcanzados. No copia necesariamente sus valores; mantiene acceso a las mismas variables. El recolector libera el entorno cuando ya no existe una ruta alcanzable hacia él.
Puntos importantes
Los closures se crean al definir funciones, no solo al retornarlas.
Capturan bindings, no snapshots automáticos.
Permiten encapsulación, factories, memoización y callbacks.
También pueden retener memoria o producir valores inesperados en ciclos.
Ejemplo
function createCounter() {
let count = 0;
return () => ++count;
}
const counter = createCounter();
console.log(counter());
console.log(counter());
Resultado1
2
Qué está pasando
createCounter terminó, pero la función retornada mantiene acceso al binding count. Cada llamada modifica ese mismo binding privado.
Errores comunes
Decir que closure es “una función dentro de otra”; eso no explica la persistencia.
Pensar que captura una copia fija del valor.
Conservar listeners o callbacks que retienen objetos grandes.
Usar var en ciclos y sorprenderse porque todas las funciones observan el valor final.
Pregunta típica de entrevista
¿Qué es un closure y qué casos reales tiene?
Respuesta Junior
Es una función que recuerda variables de su scope exterior.
Respuesta Mid
Es una función junto con su entorno léxico, lo que permite acceder a bindings exteriores después de que terminó la función que los creó.
Respuesta Senior
Es una propiedad fundamental del modelo léxico. El motor conserva los entornos alcanzables, lo que habilita encapsulación y composición, pero también exige administrar referencias de larga vida. En React aparece continuamente: handlers y efectos cierran sobre los valores del render que los creó, lo que explica stale closures.
Follow-up frecuente: ¿Cómo puede un closure causar un memory leak o un stale closure en React?
El entrevistador quiere saber…
Quiere saber si puedes conectar lenguaje, memoria y problemas reales como stale state o listeners sin limpiar.
JavaScript siempre pasa argumentos por valor. La confusión aparece porque, para objetos, ese valor es una referencia. Copiar la referencia crea dos bindings que apuntan al mismo objeto.
18 min
🟢 Repaso rápido🟡 Deep dive🔴 Senior notes
¿Qué es y por qué existe?
JavaScript siempre pasa argumentos por valor. La confusión aparece porque, para objetos, ese valor es una referencia. Copiar la referencia crea dos bindings que apuntan al mismo objeto.
Cómo funciona internamente
Los primitivos se almacenan y comparan por su valor. Los objetos tienen identidad; una variable contiene una referencia a esa identidad. Spread y Object.assign crean copias superficiales: duplican el primer nivel, pero las referencias anidadas siguen compartidas.
Puntos importantes
Dos objetos con el mismo contenido no son estrictamente iguales.
La asignación de un objeto copia la referencia.
Spread no realiza deep clone.
structuredClone sirve para muchos datos estructurados, pero no para funciones ni ciertos objetos especiales.
El objeto exterior sí es nuevo, pero profile conserva la misma referencia. Por eso cambiar el nombre se refleja en ambos.
Errores comunes
Decir “los objetos se pasan por referencia”; técnicamente se pasa por valor una referencia.
Confiar en JSON.parse(JSON.stringify()) para todos los deep clones.
Mutar estado de React después de una copia superficial incompleta.
Comparar objetos por contenido con ===.
Pregunta típica de entrevista
¿JavaScript pasa objetos por referencia o por valor?
Respuesta Junior
Los objetos se pasan por referencia.
Respuesta Mid
JavaScript pasa todo por valor; en objetos, el valor pasado es una referencia al mismo objeto.
Respuesta Senior
Usaría la expresión “pass-by-sharing” para describir el efecto observable, pero aclararía que el parámetro recibe una copia de la referencia. Reasignar el parámetro no cambia el binding original, mientras mutar el objeto sí afecta la identidad compartida. Esta distinción es clave para inmutabilidad, memoización y render de React.
Follow-up frecuente: ¿Cuándo usarías structuredClone, una librería especializada o una actualización inmutable manual?
El entrevistador quiere saber…
La precisión importa. Esta pregunta suele revelar si entiendes identidad y por qué React depende de cambios de referencia.
La igualdad estricta === compara sin coerción de tipos; la abstracta == aplica reglas de conversión antes de comparar. Object.is es similar a ===, pero trata NaN como igual a sí mismo y distingue +0 de -0.
17 min
🟢 Repaso rápido🟡 Deep dive🔴 Senior notes
¿Qué es y por qué existe?
La igualdad estricta === compara sin coerción de tipos; la abstracta == aplica reglas de conversión antes de comparar. Object.is es similar a ===, pero trata NaN como igual a sí mismo y distingue +0 de -0.
Cómo funciona internamente
La coerción usa algoritmos de conversión como ToPrimitive, ToNumber y ToString. En objetos, primero intenta obtener un valor primitivo mediante Symbol.toPrimitive, valueOf o toString según el contexto.
Puntos importantes
Prefiere === salvo que exista una razón concreta y documentada.
null == undefined es true, pero no son estrictamente iguales.
La igualdad abstracta convierte false y la cadena vacía a cero en algunos pasos. El arreglo vacío se convierte a "" mediante su representación primitiva. Object.is corrige el caso especial de NaN.
Errores comunes
Memorizar resultados sin aprender las reglas generales.
Usar == accidentalmente en validaciones.
Comparar arrays u objetos por contenido con ===.
Comprobar NaN con value === NaN en vez de Number.isNaN.
Pregunta típica de entrevista
¿Cuál es la diferencia entre ==, === y Object.is?
Respuesta Junior
== convierte tipos; === no. Object.is es otra forma de comparar.
Respuesta Mid
=== evita coerción. Object.is coincide casi siempre, pero considera NaN igual a NaN y +0 distinto de -0.
Respuesta Senior
Además explicaría que objetos se comparan por identidad y que la igualdad abstracta sigue un algoritmo definido, no conversiones “aleatorias”. En producción prefiero ===, usando == solo en patrones deliberados como value == null cuando el equipo acepta cubrir null y undefined conjuntamente.
Follow-up frecuente: ¿Por qué React usa semántica equivalente a Object.is en varias comparaciones de estado y dependencias?
El entrevistador quiere saber…
Quiere comprobar si razonas conversiones, no si sabes una lista de trucos.
Las funciones son objetos invocables de primera clase: pueden almacenarse, pasarse y retornarse. Las declarations, expressions y arrow functions difieren en hoisting, this, arguments, constructibilidad y sintaxis.
18 min
🟢 Repaso rápido🟡 Deep dive🔴 Senior notes
¿Qué es y por qué existe?
Las funciones son objetos invocables de primera clase: pueden almacenarse, pasarse y retornarse. Las declarations, expressions y arrow functions difieren en hoisting, this, arguments, constructibilidad y sintaxis.
Cómo funciona internamente
Una función tradicional obtiene this según la llamada y puede tener arguments y [[Construct]]. Una arrow no crea sus propios this, arguments, super ni new.target; los resuelve léxicamente desde el entorno exterior y no puede usarse con new.
Puntos importantes
Function declarations están disponibles antes de su línea.
Arrow functions son ideales para callbacks que necesitan this léxico.
Los métodos de objeto normalmente no deberían ser arrows si requieren this dinámico.
Toda función puede cerrar sobre su entorno léxico.
regular recibe team como this porque se invoca como método. arrow capturó this del scope exterior al crearse; no obtiene team por estar escrita dentro del literal.
Errores comunes
Usar arrow como método esperando que this sea el objeto.
Elegir arrow solo por ser más corta.
Confundir la función con el valor retornado.
Olvidar que una callback puede perder el receptor original.
Pregunta típica de entrevista
¿Cuándo no usarías una arrow function?
Respuesta Junior
Cuando necesito this.
Respuesta Mid
Cuando necesito this dinámico, arguments, usar la función como constructor o ciertos métodos de prototipo.
Respuesta Senior
Las arrows son una herramienta de semántica, no únicamente de estilo. Las evitaría para métodos que dependen del receptor, constructores y APIs que deliberadamente enlazan this. Las usaría en callbacks donde quiero capturar el contexto exterior y reducir errores de rebinding.
Follow-up frecuente: ¿Qué ocurre con bind, call y apply cuando se usan sobre una arrow function?
El entrevistador quiere saber…
El entrevistador busca que conozcas las diferencias semánticas, no una preferencia estética.
En funciones tradicionales, this no se decide por dónde se escribió la función, sino principalmente por cómo se invoca. call y apply ejecutan con un this explícito; bind crea una nueva función enlazada.
20 min
🟢 Repaso rápido🟡 Deep dive🔴 Senior notes
¿Qué es y por qué existe?
En funciones tradicionales, this no se decide por dónde se escribió la función, sino principalmente por cómo se invoca. call y apply ejecutan con un this explícito; bind crea una nueva función enlazada.
Cómo funciona internamente
La resolución típica sigue prioridades: new binding, explicit binding con call/apply/bind, implicit binding mediante objeto.método(), y default binding. Las arrow functions ignoran estas reglas porque capturan this léxicamente.
Puntos importantes
Extraer un método puede perder su receptor.
bind no ejecuta inmediatamente; retorna otra función.
call recibe argumentos separados y apply un arreglo o array-like.
Un bound function usada con new prioriza el nuevo objeto sobre el this enlazado.
La referencia greet ya no conserva automáticamente user como receptor. call lo establece durante una ejecución; bind crea una función reutilizable con user y el primer argumento preconfigurados.
Errores comunes
Decir que this apunta siempre al objeto donde fue declarada la función.
Perder this al pasar métodos como callbacks.
Intentar cambiar this de una arrow con bind.
Confundir apply con spread moderno sin entender la diferencia semántica.
Pregunta típica de entrevista
¿Cómo se determina this en JavaScript?
Respuesta Junior
Depende del contexto.
Respuesta Mid
Depende de la forma de invocación: constructor, enlace explícito, llamada como método o llamada directa. Las arrows lo capturan léxicamente.
Respuesta Senior
Explicaría las reglas por prioridad y separaría lexical scope de call-site binding. También señalaría matices de strict mode, módulos y funciones enlazadas. En código moderno prefiero evitar APIs ambiguas y usar closures o métodos claramente enlazados cuando mejoran legibilidad.
Follow-up frecuente: ¿Qué valor tiene this en una llamada directa dentro de un ES module y cómo difiere de un script no estricto?
El entrevistador quiere saber…
Quiere verificar si puedes predecir this sin recurrir a “depende” como respuesta final.
El Event Loop coordina la ejecución de JavaScript con las colas de trabajo del entorno. El motor ejecuta una tarea hasta vaciar el call stack; después procesa las microtareas pendientes antes de permitir render y tomar otra tarea.
25 min
🟢 Repaso rápido🟡 Deep dive🔴 Senior notes
¿Qué es y por qué existe?
El Event Loop coordina la ejecución de JavaScript con las colas de trabajo del entorno. El motor ejecuta una tarea hasta vaciar el call stack; después procesa las microtareas pendientes antes de permitir render y tomar otra tarea.
Cómo funciona internamente
El navegador mantiene task queues para eventos, timers y otras fuentes, además de una microtask queue usada por callbacks de Promise y queueMicrotask. Tras cada tarea se ejecuta un microtask checkpoint. Un exceso de microtareas puede retrasar render y otras tareas.
Puntos importantes
JavaScript no ejecuta dos frames del mismo agente simultáneamente.
Promise.then y continuaciones de await son microtareas.
setTimeout agenda una tarea, no garantiza tiempo exacto.
Render normalmente ocurre entre tareas, no en medio de una pila ocupada.
El script inicial es una tarea. A y E son síncronos. Las microtareas se ejecutan en el orden en que fueron encoladas al terminar la tarea. El timer queda para una tarea posterior.
Errores comunes
Decir que setTimeout(0) se ejecuta inmediatamente.
Llamar “Web APIs” a toda la arquitectura sin distinguir navegador y motor.
Olvidar que las microtareas pueden crear más microtareas antes de ceder.
Bloquear el main thread con trabajo CPU intensivo.
Pregunta típica de entrevista
Explica el orden de ejecución entre código síncrono, Promise y setTimeout.
Respuesta Junior
Primero lo síncrono, luego Promise y después setTimeout.
Respuesta Mid
El script corre como tarea; al vaciarse la pila se drenan las microtareas de Promise; luego el loop toma la tarea del timer.
Respuesta Senior
Además diferenciaría task sources, microtask checkpoint y oportunidades de render. Señalaría que el tiempo de un timer es un mínimo aproximado y que long tasks o cadenas de microtareas afectan INP. Para CPU intensivo consideraría dividir trabajo, scheduling cooperativo o Web Workers.
Follow-up frecuente: ¿Puede una cadena infinita de Promise.then impedir que se ejecute un setTimeout o que el navegador pinte?
El entrevistador quiere saber…
Quiere comprobar si puedes predecir orden, diagnosticar UI bloqueada y conectar asincronía con performance.
Una Promise representa el resultado eventual de una operación. async/await es sintaxis construida sobre promesas que permite expresar flujos asíncronos con estructura similar al código secuencial.
24 min
🟢 Repaso rápido🟡 Deep dive🔴 Senior notes
¿Qué es y por qué existe?
Una Promise representa el resultado eventual de una operación. async/await es sintaxis construida sobre promesas que permite expresar flujos asíncronos con estructura similar al código secuencial.
Cómo funciona internamente
Una Promise pasa de pending a fulfilled o rejected una sola vez. Los handlers de then/catch/finally se programan como microtareas. Una función async siempre retorna una Promise; await suspende esa ejecución y agenda su continuación cuando el valor se resuelve.
Puntos importantes
Promise.all falla rápido; allSettled espera todos los resultados.
await secuencial puede desperdiciar concurrencia cuando las operaciones son independientes.
catch captura rechazos dentro de la cadena que lo precede.
AbortController es clave para cancelar trabajo soportado por APIs como fetch.
Las dos operaciones empiezan antes del await y avanzan concurrentemente. Promise.all conserva el orden de entrada aunque orders termine primero.
Errores comunes
Usar await dentro de un loop cuando las llamadas podrían ser paralelas.
Olvidar manejar rechazo y cancelación.
Construir new Promise alrededor de una API que ya devuelve Promise.
Confundir concurrencia con ejecución paralela de JavaScript en el mismo hilo.
Pregunta típica de entrevista
¿Qué diferencia hay entre Promise.all, allSettled, race y any?
Respuesta Junior
Todas combinan varias promesas, pero resuelven en momentos distintos.
Respuesta Mid
all cumple cuando todas cumplen y rechaza con la primera; allSettled espera todas; race se asienta con la primera asentada; any cumple con la primera cumplida y rechaza si todas fallan.
Respuesta Senior
La elección expresa la semántica del negocio. También discutiría cancelación, límites de concurrencia, errores parciales y que Promise.all no cancela automáticamente las operaciones restantes. En UI evitaría race conditions mediante AbortController, identificadores de solicitud o librerías de server state.
Follow-up frecuente: ¿Cómo evitas que una respuesta antigua sobrescriba una búsqueda más reciente en una interfaz?
El entrevistador quiere saber…
No basta conocer la sintaxis; se evalúan concurrencia, errores, cancelación y consistencia de datos.