DavidGadyan

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.

  1. 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() {}
  2. 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
  3. 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; }, {});
  4. 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!)
  5. 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
  6. 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 ?? '—';
  7. 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);
  8. 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
  9. 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
  10. 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)]);
  11. 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
  12. 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.