JavaScript: chuleta de sintaxis y quiz de práctica
Doce patrones de sintaxis detrás de la mayoría de preguntas de JavaScript en pruebas técnicas: arrays, objetos, closures, promesas y el event loop, con un quiz de práctica de 20 preguntas.
Chuleta de sintaxis
12 patrones. Elige uno en el mapa para ver su sintaxis, cuándo usarlo y la trampa que esconde.
Básicos
const, let y var
Ámbito y reasignación
- Sintaxis
const x = 10; // no reassignment (an object inside can still change) let y = 20; // block-scoped, reassignable var z = 30; // function-scoped, hoisted as undefined: avoid- Cuándo usarlo
- Usa const por defecto; cambia a let solo cuando la variable deba reasignarse (contadores, índices de bucle). Nunca var en código nuevo.
- Qué hace
- const y let tienen ámbito de bloque y están en la zona muerta temporal hasta que se ejecuta su línea. var tiene ámbito de función y se eleva (hoisting) al principio con valor undefined. Las declaraciones de función se elevan enteras, así que pueden llamarse antes de su línea.
- Cuidado con
- const congela la variable, no el valor: const user = {} sigue permitiendo user.name = "x". Leer un let antes de su declaración lanza ReferenceError; leer un var da undefined.
- Ejemplo
- console.log(a); // undefined (var is hoisted) var a = 1; console.log(b); // ReferenceError (temporal dead zone) let b = 2; hello(); // works: function declarations hoist whole function hello() {}
Básicos
=== y truthy / falsy
Comparación estricta
- Sintaxis
5 === "5" // false (value AND type) 5 == "5" // true (coerces: avoid) // falsy: false, 0, -0, "", null, undefined, NaN // truthy: everything else, including [], {}, "0", "false"- Cuándo usarlo
- En cada comparación. El único uso aceptado de == es x == null, que coincide con null y con undefined.
- Qué hace
- === compara valor y tipo sin coerción. Los contextos booleanos (if, &&, ||, !) convierten el valor según la lista de falsy: siete valores falsy, todo lo demás es truthy.
- Cuidado con
- Un array vacío y un objeto vacío son truthy, así que if (arr) no comprueba si está vacío: usa arr.length === 0. NaN !== NaN, usa Number.isNaN(). [] == false es true por la coerción.
- Ejemplo
- [] == false // true (coercion) [] === false // false if (arr.length === 0) {} // the right emptiness check Number.isNaN(NaN) // true null == undefined // true; null === undefined // false
Arrays
map, filter, reduce
Transformar, filtrar, plegar
- Sintaxis
nums.map(x => x * 2) // same length, new values nums.filter(x => x % 2 === 0) // a subset nums.reduce((acc, x) => acc + x, 0) // one value- Cuándo usarlo
- map para transformar cada elemento, filter para quedarte con un subconjunto, reduce para producir un solo valor: una suma, un objeto de búsqueda, datos agrupados.
- Qué hace
- Los tres devuelven un array o valor nuevo y no tocan el original, así que se encadenan de arriba abajo como una tubería. El callback recibe (elemento, índice, array).
- Cuidado con
- Pasa siempre un valor inicial a reduce: sin él, el primer elemento hace de acumulador y un array vacío lanza error. Un callback de map con llaves y sin return da un array de undefined. forEach devuelve undefined: no se encadena.
- Ejemplo
- const total = orders .filter(o => o.status === 'completed') .map(o => o.amount) .reduce((sum, a) => sum + a, 0); const byId = users.reduce((acc, u) => { acc[u.id] = u; return acc; }, {});
Arrays
find, some, every, sort
Buscar y ordenar
- Sintaxis
users.find(u => u.id === 5) // first match or undefined users.some(u => u.age >= 18) // at least one → boolean users.every(u => u.age >= 18) // all → boolean [1, 2, 3].includes(2) // true nums.sort((a, b) => a - b) // ascending numbers (in place)- Cuándo usarlo
- find para un elemento, some y every para comprobaciones sí/no, includes para primitivos, sort con comparador para todo lo numérico o por campo.
- Qué hace
- find y some se detienen en el primer acierto; every se detiene en el primer fallo. sort reordena el array en el sitio según el signo del comparador: negativo deja a antes de b.
- Cuidado con
- sort() sin comparador ordena como cadenas (10 antes que 9) y muta el array: copia primero con [...nums].sort() o usa toSorted(). findIndex devuelve -1 si no hay coincidencia, find devuelve undefined.
- Ejemplo
- const sorted = [...users].sort((a, b) => a.age - b.age); // original untouched const desc = [...nums].sort((a, b) => b - a); users.sort((a, b) => a.name.localeCompare(b.name)); [10, 9, 1].sort() // [1, 10, 9] (string order!)
Objetos
Spread y destructuring
Copiar, combinar, desempaquetar
- Sintaxis
const b = [...a, 3, 4]; // array copy + append const updated = { ...user, age: 34 }; // object copy + override const { name, age = 0 } = user; // unpack with a default const [first, , third] = list; // skip a position const { id, ...rest } = obj; // rest must be last function User({ name, age }) {} // destructured parameter- Cuándo usarlo
- Actualizaciones inmutables (estado de React), combinar valores por defecto con opciones, extraer campos de respuestas de API y de props.
- Qué hace
- Spread copia las propiedades propias enumerables o los elementos iterables en un array u objeto nuevo; las claves posteriores sobrescriben las anteriores. Destructuring asigna variables a posiciones o claves en una línea.
- Cuidado con
- Spread es superficial: los objetos anidados siguen siendo referencias compartidas. Rest debe ir el último. Desestructurar una clave inexistente da undefined salvo que añadas un valor por defecto.
- Ejemplo
- const defaults = { theme: 'light', lang: 'en' }; const opts = { ...defaults, lang: 'es' }; // { theme: 'light', lang: 'es' } const { data: { items = [] } = {} } = response; // nested with defaults const [x, y] = [y, x]; // swap
Objetos
?. y ??
Acceso seguro, valores por defecto
- Sintaxis
user?.address?.city // undefined instead of a TypeError user.getName?.() // call only if it exists items?.[0] const name = user.name ?? 'Unknown'; // only null / undefined fall back count ??= 10; // assign only when null / undefined- Cuándo usarlo
- Leer datos anidados de una API que pueden faltar; valores por defecto donde 0, "" o false son válidos.
- Qué hace
- ?. corta y devuelve undefined cuando el lado izquierdo es null o undefined. ?? devuelve el lado derecho solo para null o undefined, a diferencia de ||, que también sustituye 0, "" y false.
- Cuidado con
- 0 || 10 es 10, pero 0 ?? 10 es 0. El encadenamiento opcional no captura errores lanzados dentro de la llamada, solo un receptor ausente. Mezclar ?? con || o && exige paréntesis.
- Ejemplo
- const port = config.port ?? 3000; // keeps an explicit 0 const label = config.label || 'n/a'; // '' becomes 'n/a' const city = order?.customer?.address?.city ?? '—';
Objetos
Object.keys, values, entries
Recorrer objetos
- Sintaxis
Object.keys(obj) // ['a', 'b'] Object.values(obj) // [1, 2] Object.entries(obj) // [['a', 1], ['b', 2]] Object.fromEntries(pairs) Object.hasOwn(obj, 'a')- Cuándo usarlo
- Recorrer un objeto de búsqueda, convertir entre arrays y objetos, filtrar o mapear los campos de un objeto.
- Qué hace
- Los tres accesores devuelven arrays con las claves propias enumerables en orden de inserción, así que todos los métodos de array quedan disponibles; fromEntries invierte entries.
- Cuidado con
- Las claves son cadenas aunque parezcan números. for...in recorre también las propiedades heredadas: prefiere Object.keys. Usa Object.hasOwn en vez de obj.hasOwnProperty.
- Ejemplo
- const prices = { a: 10, b: 25, c: 7 }; const expensive = Object.fromEntries( Object.entries(prices).filter(([, p]) => p > 9) ); // { a: 10, b: 25 } for (const [k, v] of Object.entries(prices)) console.log(k, v);
Colecciones
Map y Set
Contar y deduplicar
- Sintaxis
const freq = new Map(); for (const x of arr) freq.set(x, (freq.get(x) ?? 0) + 1); freq.has(x); freq.size; [...freq.entries()] const unique = [...new Set(arr)];- Cuándo usarlo
- Conteos de frecuencia, búsquedas O(1) por cualquier tipo de clave, eliminar duplicados, cachear resultados, detectar duplicados en una pasada.
- Qué hace
- Map conserva el orden de inserción, admite claves objeto y tiene get, set y has en O(1) de media. Set guarda valores únicos con las mismas operaciones. Ambos se recorren con for...of.
- Cuidado con
- Map.get con una clave inexistente devuelve undefined, de ahí el ?? 0. Las claves objeto se comparan por referencia, no por contenido: Set no deduplica objetos iguales. Un objeto normal también sirve de contador, pero convierte las claves en cadenas.
- Ejemplo
- const count = {}; for (const w of words) count[w] = (count[w] || 0) + 1; const hasDuplicate = new Set(nums).size !== nums.length; const top = [...freq.entries()].sort((a, b) => b[1] - a[1])[0]; // most frequent
Funciones
Closures, this y debounce
Funciones que recuerdan
- Sintaxis
function counter() { let count = 0; return () => ++count; // remembers count } const inc = counter(); inc(); inc(); // 2 function debounce(fn, delay) { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => fn(...args), delay); }; }- Cuándo usarlo
- Estado privado sin clases, memoización, manejadores que necesitan contexto, buscadores (debounce) y manejadores de scroll o resize (throttle).
- Qué hace
- Una función interna mantiene una referencia viva a su ámbito exterior después de que la función externa haya terminado. Las arrow functions toman this del ámbito que las rodea; las funciones normales reciben this según cómo se llaman.
- Cuidado con
- Debounce se dispara una vez cuando el usuario para; throttle como mucho una vez por intervalo. Un var en un bucle comparte una sola variable entre todos los closures; let crea una por iteración.
- Ejemplo
- function throttle(fn, delay) { let last = 0; return (...args) => { const now = Date.now(); if (now - last >= delay) { last = now; fn(...args); } }; } const search = debounce(q => fetch('/api?q=' + q), 300); // one request after typing stops
Asíncrono
Promesas y async / await
Esperar sin bloquear
- Sintaxis
async function getUsers() { try { const res = await fetch('/api/users'); if (!res.ok) throw new Error('Request failed'); return await res.json(); } catch (err) { console.error(err); } } const [users, posts] = await Promise.all([getUsers(), getPosts()]);- Cuándo usarlo
- Cualquier E/S: llamadas HTTP, temporizadores, lectura de archivos. Promise.all para peticiones independientes, allSettled cuando alguna puede fallar, race para timeouts, any para el primer éxito.
- Qué hace
- await pausa la función async, no el hilo, y extrae el valor de la promesa. Una función async siempre devuelve una promesa; un error lanzado se convierte en un rechazo que recibe .catch o try/catch.
- Cuidado con
- fetch solo rechaza si falla la red: comprueba res.ok para 404 y 500. Promise.all rechaza en cuanto una promesa rechaza. await dentro de un for se ejecuta en secuencia; reúne las promesas y usa Promise.all para paralelizar.
- Ejemplo
- Promise.resolve(1).then(x => x + 1) // resolves to 2 const results = await Promise.allSettled(tasks); const ok = results.filter(r => r.status === 'fulfilled').map(r => r.value); await Promise.race([fetchData(), timeout(5000)]);
Asíncrono
Orden del event loop
Síncrono, microtareas, macrotareas
- Sintaxis
console.log('A'); setTimeout(() => console.log('B'), 0); // macrotask Promise.resolve().then(() => console.log('C')); // microtask console.log('D'); // A D C B- Cuándo usarlo
- En cada pregunta de «¿qué se imprime?» y en cada bug donde un callback se ejecuta más tarde de lo esperado.
- Qué hace
- La pila ejecuta el código síncrono hasta el final, después todas las microtareas en cola (callbacks de promesas, queueMicrotask), después una macrotarea (setTimeout, setInterval, E/S), después otra vez las microtareas, y así sucesivamente.
- Cuidado con
- setTimeout(fn, 0) nunca es inmediato. Un await parte la función: el código posterior es una microtarea. Un bucle síncrono largo bloquea todo, incluido el renderizado y los temporizadores.
- Ejemplo
- async function f() { console.log(1); await null; console.log(3); } f(); console.log(2); // 1 2 3
Patrones
Dos punteros, ventana deslizante, Big-O
De O(n²) a O(n)
- Sintaxis
let l = 0, r = arr.length - 1; // two pointers while (l < r) { /* compare arr[l], arr[r] */ l++; r--; } let left = 0; // sliding window for (let right = 0; right < arr.length; right++) { while (/* window invalid */) left++; // arr[left..right] is a valid window }- Cuándo usarlo
- Pares en un array ordenado, palíndromos, subcadena más larga sin repetidos, suma máxima de k elementos, duplicados en O(n).
- Qué hace
- Dos punteros avanzan hacia dentro o al compás sobre datos ordenados. Una ventana deslizante mantiene un rango válido y solo mueve sus extremos hacia delante. Un Set o Map convierte una búsqueda con bucles anidados en una sola pasada.
- Cuidado con
- Indexar un array, push, pop y las operaciones de Map/Set son O(1); shift y unshift son O(n); ordenar es O(n log n). Usa un índice de cabeza en vez de shift() para una cola.
- Ejemplo
- function isPalindrome(s) { const t = s.toLowerCase().replace(/[^a-z0-9]/g, ''); let l = 0, r = t.length - 1; while (l < r) { if (t[l++] !== t[r--]) return false; } return true; } const seen = new Set(); const firstDup = nums.find(n => seen.has(n) || (seen.add(n), false));
Elige un patrón en el mapa para ver su sintaxis, cuándo usarlo, qué hace y con qué tener cuidado.
Quiz de práctica
20 preguntas tipo test elegidas al azar entre las 40 de este tema, una por pantalla, corregidas al final. Cada pregunta remite a un patrón del mapa.
Elige una respuesta, muévete libremente entre las preguntas y termina cuando quieras: la puntuación y la explicación de cada pregunta aparecen al final.
- 20 preguntas
- Sin límite de tiempo
- Corrección al final
El quiz necesita JavaScript. La chuleta de arriba funciona sin él.
Material de práctica para pruebas técnicas cronometradas (HackerRank, Codility, TestGorilla y similares). El comportamiento descrito sigue la especificación ECMAScript tal como la implementan los navegadores actuales y Node.js.