Volumen 1 · De fundamentos a Senior

JavaScript

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.

Ejemplo

const user = { name: "Erick" };
user.name = "Alan"; // permitido

let attempts = 1;
attempts += 1;

console.log(user.name, attempts);

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.

Conceptos relacionados

Volver arriba ↑
★★★★★Junior → Senior🔥 Fundamental

Scope y entorno léxico

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();

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.

Conceptos relacionados

Volver arriba ↑
★★★★★Junior → Senior🔥 Sale muchísimo

Hoisting y Temporal Dead Zone

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;

Errores comunes

  • Explicar hoisting diciendo únicamente “JavaScript sube variables”.
  • Afirmar que let y const no tienen hoisting.
  • Confundir ReferenceError con undefined.
  • 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.

Conceptos relacionados

Volver arriba ↑
★★★★★Mid → Senior🔥 Imprescindible

Closures

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());

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.

Conceptos relacionados

Volver arriba ↑
★★★★★Junior → Senior🔥 Muy preguntado

Valores, referencias y copias

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.

Ejemplo

const original = { profile: { name: "Erick" } };
const shallow = { ...original };

shallow.profile.name = "Alan";

console.log(original.profile.name);
console.log(original === shallow);
console.log(original.profile === shallow.profile);

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.

Conceptos relacionados

Volver arriba ↑
★★★★★Junior → Senior🔥 Clásico de entrevista

Igualdad y coerción

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.
  • NaN no es igual a sí mismo con ===.
  • Los objetos se comparan por identidad.

Ejemplo

console.log(0 == false);
console.log(0 === false);
console.log(NaN === NaN);
console.log(Object.is(NaN, NaN));
console.log([] == "");

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.

Conceptos relacionados

Volver arriba ↑
★★★★★Junior → Senior🔥 Fundamental

Funciones, declaraciones y arrow functions

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.

Ejemplo

const team = {
  name: "Frontend",
  regular() { return this.name; },
  arrow: () => this?.name
};

console.log(team.regular());
console.log(team.arrow());

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.

Conceptos relacionados

Volver arriba ↑
★★★★★Mid → Senior🔥 Muy preguntado

this, call, apply y bind

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.

Ejemplo

const user = {
  name: "Erick",
  greet(prefix) { return `${prefix} ${this.name}`; }
};

const greet = user.greet;
console.log(greet.call(user, "Hola"));
console.log(greet.bind(user, "Qué tal")());

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.

Conceptos relacionados

Volver arriba ↑
★★★★★Mid → Senior🔥 Obligatorio

Event Loop, tareas y microtareas

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.

Ejemplo

console.log("A");

setTimeout(() => console.log("B"), 0);
Promise.resolve().then(() => console.log("C"));
queueMicrotask(() => console.log("D"));

console.log("E");

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.

Conceptos relacionados

Volver arriba ↑
★★★★★Mid → Senior🔥 Obligatorio

Promises y async/await

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.

Ejemplo

const wait = (value, ms) =>
  new Promise(resolve => setTimeout(() => resolve(value), ms));

async function load() {
  const [user, orders] = await Promise.all([
    wait("user", 20),
    wait("orders", 10)
  ]);
  return `${user}:${orders}`;
}

load().then(console.log);

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.

Conceptos relacionados

Volver arriba ↑