{
  "assessmentTests": {
    "javascript_test": {
      "name": "Prueba de JavaScript",
      "desc": "30 preguntas de escenario sobre sintaxis core y coerción, funciones/alcance/cierres y this, objetos/arrays y estructuras de datos, y bucle de eventos/async/idiomas prototipo — descubra si sus habilidades de JavaScript coinciden con lo que los anuncios de trabajo entienden por competencia de JavaScript.",
      "recommendation": "Tu perfil de habilidades de JavaScript",
      "results": {
        "beginner": {
          "name": "Principiante",
          "desc": "Puedes escribir funciones y objetos que funcionan, pero las preguntas que perdiste se agrupan alrededor de la coerción de tipos y la semántica de referencia en lugar de la sintaxis — == convierte tipos silenciosamente antes de comparar, una asignación de array que comparte un objeto en lugar de copiarlo, por qué NaN nunca es igual a sí mismo. Nada de esto se trata de ser malo en JavaScript; estas son las reglas específicas que confunden a las personas que aprendieron el lenguaje escribiendo código que pasaba a funcionar en lugar de aprender cómo se comportan realmente los valores y referencias. Son importantes en el trabajo porque cada una es un lugar donde el código se ejecuta sin error y aún hace lo incorrecto.",
          "recommendation": "Comienza con tres cosas, en este orden: por qué == coerces tipos antes de comparar mientras === no, por qué asignar un array u objeto a una nueva variable copia la referencia en lugar del valor, y cómo var se eleva e inicializa a undefined mientras let y const permanecen en la zona muerta temporal hasta que se ejecuta su declaración. La guía de JavaScript de MDN cubre las tres con ejemplos ejecutables."
        },
        "intermediate": {
          "name": "Intermedio",
          "desc": "Manejas el código de aplicación diaria cómodamente — funciones, métodos de array, flujo de control directo — y no serías ralentizado por trabajo de mantenimiento rutinario. La brecha entre aquí y Avanzado se trata principalmente de lo que sucede cuando las reglas de alcance y tiempo de JavaScript interactúan: un var capturado dentro de callbacks de bucle todos leyendo el mismo valor final, un this que silenciosamente deja de apuntar al objeto una vez que el método se desacopla de él, una copia spread que se ve profunda pero solo copia un nivel. Estos son los tipos de errores que pasan una lectura rápida y solo aparecen bajo una condición de tiempo de ejecución específica.",
          "recommendation": "Enfócate en cómo se resuelven el alcance y this en tiempo de llamada en lugar de tiempo de definición: por qué un bucle for basado en var entrega a cada callback el mismo valor de contador final mientras let no, cómo se determina this por cómo se llama una función en lugar de dónde se escribe, y por qué una copia spread u Object.assign solo copia el nivel superior de un objeto. Luego la Event Loop, porque eso es lo que confunde a personas que ya conocen Promises individualmente."
        },
        "advanced": {
          "name": "Avanzado",
          "desc": "Este es el nivel que la mayoría de anuncios de trabajo entienden por \"JavaScript fuerte\". Lees una cadena de filter/map/reduce y puedes predecir exactamente qué devuelve sin mutar el array original, sabes por qué un console.log sincrónico siempre vence a una microtarea que siempre vence a un setTimeout, y recurres a Promise.all sabiendo que rechaza instantáneamente si cualquier promesa rechaza en lugar de esperar al resto. Lo que separa este nivel del más alto es el lado prototipo y modelo de objeto del trabajo: cómo la cadena de prototipo resuelve una búsqueda de método heredado versus hasOwnProperty, y qué un campo de clase privada realmente oculta.",
          "recommendation": "Presiona en las partes que protegen el estado que otro código también toca: cómo la cadena de prototipos de JavaScript resuelve búsquedas de método que no están en la instancia misma, qué campos privados #fields de una clase realmente ocultan del acceso externo, y cómo JSON.stringify trata undefined y funciones de manera diferente dentro de un objeto plano versus dentro de un array. El material de MDN sobre la Event Loop y la cadena de prototipo es la siguiente parada natural para ambos."
        },
        "expert": {
          "name": "Experto",
          "desc": "Puntuaste en la parte superior de cada sección — sintaxis core, tipos y coerción; funciones, alcance, cierres y this; objetos, arrays y estructuras de datos; y async, Event Loop, prototipos e idiomas. Prácticamente, eso significa que puedes recibir una función extraña y explicar por qué devuelve lo que devuelve, no solo lo que la sintaxis dice que debería hacer, que es la habilidad más difícil y valiosa. En este nivel, el lenguaje en sí raramente es el factor limitante; el límite suele ser el flujo de control asincrónico o la forma de los datos debajo.",
          "recommendation": "Los retornos ahora están en diseño y diagnóstico: razonamiento sobre una condición de carrera entre Promise.all y Promise.race en lugar de recitar sus definiciones, elegir cuándo un cierre es la herramienta correcta para estado privado versus cuándo el campo privado de una clase lo es, y leer un error de ordenamiento microtarea-vs-macrotarea en registros de producción en lugar de en un cuestionario. Si estás siendo examinado para un rol, describe un error de tiempo que encontraste — una variable de cierre obsoleta o un callback asincrónico fuera de orden — en lugar de nombrar características de JavaScript; demuestra el razonamiento, no solo el vocabulario."
        }
      },
      "questions": [
        {
          "question": "console.log(x);\nvar x = 5;\n¿Qué imprime esto?",
          "options": [
            {
              "icon": "",
              "label": "5 — las declaraciones var se ejecutan inmediatamente, antes que cualquier otro código"
            },
            {
              "icon": "",
              "label": "ReferenceError: x is not defined"
            },
            {
              "icon": "",
              "label": "undefined — las declaraciones var se elevan a la parte superior de su alcance e inicializan a undefined, pero la asignación se ejecuta en su lugar, así que antes de que se ejecute la variable existe pero mantiene undefined"
            },
            {
              "icon": "",
              "label": "null"
            }
          ]
        },
        {
          "question": "console.log(y);\nlet y = 5;\n¿Qué sucede?",
          "options": [
            {
              "icon": "",
              "label": "Lanza ReferenceError: Cannot access 'y' before initialization — let se eleva a la parte superior de su alcance pero permanece en la zona muerta temporal hasta que la línea de declaración se ejecuta realmente"
            },
            {
              "icon": "",
              "label": "Imprime undefined, lo mismo que var haría"
            },
            {
              "icon": "",
              "label": "Imprime 5"
            },
            {
              "icon": "",
              "label": "Lanza un SyntaxError en tiempo de análisis"
            }
          ]
        },
        {
          "question": "const arr = [1, 2, 3];\narr.push(4);\nconsole.log(arr.length);\n¿Qué imprime esto?",
          "options": [
            {
              "icon": "",
              "label": "4 — const solo previene reasignar la propia vinculación arr, no mutar el objeto array al que apunta, así que push aún funciona"
            },
            {
              "icon": "",
              "label": "Lanza un TypeError, porque arr es const"
            },
            {
              "icon": "",
              "label": "3 — push está bloqueado en un array const"
            },
            {
              "icon": "",
              "label": "undefined"
            }
          ]
        },
        {
          "question": "console.log('' == 0);\nconsole.log('' === 0);\n¿Qué imprimen las dos líneas?",
          "options": [
            {
              "icon": "",
              "label": "true luego true"
            },
            {
              "icon": "",
              "label": "false luego false"
            },
            {
              "icon": "",
              "label": "true luego false — == coerciona la cadena vacía a 0 antes de comparar, mientras === compara tipo y valor sin coerción, y una cadena nunca es estrictamente igual a un número"
            },
            {
              "icon": "",
              "label": "false luego true"
            }
          ]
        },
        {
          "question": "console.log(NaN === NaN);\nconsole.log(Number.isNaN(NaN));\n¿Qué imprimen las dos líneas?",
          "options": [
            {
              "icon": "",
              "label": "true luego true"
            },
            {
              "icon": "",
              "label": "true luego false"
            },
            {
              "icon": "",
              "label": "false luego false"
            },
            {
              "icon": "",
              "label": "false luego true — NaN es el único valor en JavaScript que no es igual a sí mismo bajo ===, pero Number.isNaN() lo detecta correctamente de todas formas"
            }
          ]
        },
        {
          "question": "console.log(typeof null);\n¿Qué imprime esto?",
          "options": [
            {
              "icon": "",
              "label": "\"null\""
            },
            {
              "icon": "",
              "label": "\"object\" — un error antiguo en el lenguaje: typeof null devuelve \"object\" aunque null sea primitivo, y se ha mantenido por compatibilidad hacia atrás desde entonces"
            },
            {
              "icon": "",
              "label": "\"undefined\""
            },
            {
              "icon": "",
              "label": "\"boolean\""
            }
          ]
        },
        {
          "question": "¿Cuál de los siguientes es truthy cuando se usa en una condición if?",
          "options": [
            {
              "icon": "",
              "label": "0"
            },
            {
              "icon": "",
              "label": "\"\" (una cadena vacía)"
            },
            {
              "icon": "",
              "label": "null"
            },
            {
              "icon": "",
              "label": "\"0\" — una cadena no vacía siempre es truthy en JavaScript, incluso cuando su contenido es la cadena \"0\" misma"
            }
          ]
        },
        {
          "question": "foo();\nfunction foo() { console.log('a'); }\nbar();\nvar bar = function() { console.log('b'); };\n¿Qué sucede?",
          "options": [
            {
              "icon": "",
              "label": "Ambas llamadas imprimen, \"a\" luego \"b\""
            },
            {
              "icon": "",
              "label": "foo() imprime \"a\"; bar() lanza TypeError: bar is not a function — las declaraciones de función se elevan completamente con su cuerpo, pero una expresión de función asignada a var solo eleva el var mismo (como undefined), no la asignación"
            },
            {
              "icon": "",
              "label": "Ambas llamadas lanzan ReferenceError"
            },
            {
              "icon": "",
              "label": "foo() lanza; bar() imprime \"b\""
            }
          ]
        },
        {
          "question": "function makeCounter() {\n  let count = 0;\n  return function() { count++; return count; };\n}\nconst counter = makeCounter();\nconsole.log(counter());\nconsole.log(counter());\n¿Qué imprimen las dos líneas?",
          "options": [
            {
              "icon": "",
              "label": "1 luego 1"
            },
            {
              "icon": "",
              "label": "0 luego 1"
            },
            {
              "icon": "",
              "label": "undefined luego undefined"
            },
            {
              "icon": "",
              "label": "1 luego 2 — la función devuelta cierra sobre la misma variable count del alcance de makeCounter, y esa variable persiste entre llamadas en lugar de reiniciarse"
            }
          ]
        },
        {
          "question": "for (var i = 0; i < 3; i++) {\n  setTimeout(() => console.log(i), 0);\n}\n¿Qué imprime esto?",
          "options": [
            {
              "icon": "",
              "label": "0, 1, 2"
            },
            {
              "icon": "",
              "label": "3, 3, 3 — var tiene alcance de función, no de bloque, así que los tres callbacks cierran sobre la misma i, que ya ha terminado el bucle e igual a 3 en el momento que cualquiera de ellos se ejecuta"
            },
            {
              "icon": "",
              "label": "0, 0, 0"
            },
            {
              "icon": "",
              "label": "undefined, undefined, undefined"
            }
          ]
        },
        {
          "question": "for (let i = 0; i < 3; i++) {\n  setTimeout(() => console.log(i), 0);\n}\n¿Qué imprime esto?",
          "options": [
            {
              "icon": "",
              "label": "3, 3, 3"
            },
            {
              "icon": "",
              "label": "0, 0, 0"
            },
            {
              "icon": "",
              "label": "undefined, undefined, undefined"
            },
            {
              "icon": "",
              "label": "0, 1, 2 — let crea una nueva vinculación de i para cada iteración del bucle, así que cada callback cierra sobre su propia copia separada en lugar de una variable compartida"
            }
          ]
        },
        {
          "question": "function greet() { return `Hi, ${this.name}`; }\nconst person = { name: 'Ana' };\nconsole.log(greet.call(person));\n¿Qué imprime esto?",
          "options": [
            {
              "icon": "",
              "label": "\"Hi, Ana\" — Function.prototype.call invoca la función con this explícitamente establecido en su primer argumento, así que this.name se resuelve contra person"
            },
            {
              "icon": "",
              "label": "\"Hi, undefined\" — call() solo funciona en funciones que ya son métodos de un objeto"
            },
            {
              "icon": "",
              "label": "Lanza un TypeError, porque greet no es un método de person"
            },
            {
              "icon": "",
              "label": "\"Hi, ${this.name}\" — call() no evalúa el literal de plantilla"
            }
          ]
        },
        {
          "question": "const timer = {\n  seconds: 0,\n  start: function() {\n    setInterval(() => { this.seconds++; }, 1000);\n  },\n};\n¿Por qué las personas prefieren la función flecha sobre function() { this.seconds++ } dentro de setInterval aquí?",
          "options": [
            {
              "icon": "",
              "label": "Las funciones flecha no tienen su propio this — capturan this léxicamente de la función start envolvente, que es timer (ya que start fue llamada como timer.start()), así que this.seconds correctamente se refiere a timer.seconds en cada tick"
            },
            {
              "icon": "",
              "label": "Las funciones flecha se ejecutan más rápido que las funciones regulares en tiempo de ejecución"
            },
            {
              "icon": "",
              "label": "Las funciones flecha vinculan automáticamente this al objeto global, que casualmente es igual a timer"
            },
            {
              "icon": "",
              "label": "Las funciones flecha convierten this en una variable global automáticamente"
            }
          ]
        },
        {
          "question": "function multiply(a, b) { return a * b; }\nconst double = multiply.bind(null, 2);\nconsole.log(double(5));\n¿Qué imprime esto?",
          "options": [
            {
              "icon": "",
              "label": "Lanza un error, porque bind requiere ambos argumentos por adelantado"
            },
            {
              "icon": "",
              "label": "10 — bind devuelve una nueva función con this y argumentos iniciales preestablecidos; llamar double(5) proporciona el argumento restante b=5, así que multiply(2, 5) se ejecuta"
            },
            {
              "icon": "",
              "label": "NaN"
            },
            {
              "icon": "",
              "label": "7, porque bind añade sus argumentos a la llamada en lugar de sustituirlos"
            }
          ]
        },
        {
          "question": "function outer() {\n  let x = 10;\n  function inner() {\n    let x = 20;\n    console.log(x);\n  }\n  inner();\n  console.log(x);\n}\nouter();\n¿Qué imprimen las dos llamadas console.log?",
          "options": [
            {
              "icon": "",
              "label": "20 luego 20"
            },
            {
              "icon": "",
              "label": "20 luego 10 — let x de inner oscurece x de outer solo dentro del alcance de inner; x de outer nunca se toca"
            },
            {
              "icon": "",
              "label": "10 luego 10"
            },
            {
              "icon": "",
              "label": "Lanza un ReferenceError, porque x se declara dos veces"
            }
          ]
        },
        {
          "question": "const a = [1, 2, 3];\nconst b = a;\nb.push(4);\nconsole.log(a.length);\n¿Qué imprime esto?",
          "options": [
            {
              "icon": "",
              "label": "3 — b es una copia separada de a"
            },
            {
              "icon": "",
              "label": "undefined"
            },
            {
              "icon": "",
              "label": "Lanza un TypeError, porque a es const"
            },
            {
              "icon": "",
              "label": "4 — los arrays son objetos, así que const b = a copia la referencia, no el array; mutar b a través de push también muta el mismo array al que apunta a"
            }
          ]
        },
        {
          "question": "const original = { name: 'Ana', tags: ['x', 'y'] };\nconst copy = { ...original };\ncopy.name = 'Bea';\ncopy.tags.push('z');\nconsole.log(original.name, original.tags.length);\n¿Qué imprime esto?",
          "options": [
            {
              "icon": "",
              "label": "'Ana' 2 — spread clona profundamente cada objeto anidado y array"
            },
            {
              "icon": "",
              "label": "'Ana' 3 — spread es solo una copia superficial: una propiedad primitiva como name se copia por valor, así que reasignar copy.name nunca toca original, pero un array anidado como tags se copia por referencia, así que empujar en copy.tags muta el mismo array al que apunta original.tags"
            },
            {
              "icon": "",
              "label": "'Bea' 3 — spread copia todo por referencia, así que tanto los cambios de nivel superior como anidados se transfieren a original"
            },
            {
              "icon": "",
              "label": "'Bea' 2"
            }
          ]
        },
        {
          "question": "const { a: x = 10, b: y = 20 } = { a: 5 };\nconsole.log(x, y);\n¿Qué imprime esto?",
          "options": [
            {
              "icon": "",
              "label": "undefined undefined"
            },
            {
              "icon": "",
              "label": "5 undefined"
            },
            {
              "icon": "",
              "label": "10 20"
            },
            {
              "icon": "",
              "label": "5 20 — a se renombra a x y está presente, así que mantiene su valor 5; b se renombra a y y está ausente, así que vuelve a su defecto 20"
            }
          ]
        },
        {
          "question": "const [first, ...rest] = [1, 2, 3, 4];\nconsole.log(rest);\n¿Qué imprime esto?",
          "options": [
            {
              "icon": "",
              "label": "[1, 2, 3, 4]"
            },
            {
              "icon": "",
              "label": "[2, 3, 4] — rest reúne cada elemento restante después de first en un array completamente nuevo"
            },
            {
              "icon": "",
              "label": "2"
            },
            {
              "icon": "",
              "label": "[1]"
            }
          ]
        },
        {
          "question": "const nums = [10, 1, 2];\nnums.sort();\nconsole.log(nums);\n¿Qué imprime esto?",
          "options": [
            {
              "icon": "",
              "label": "[1, 2, 10] — sort siempre ordena números numéricamente en orden ascendente por defecto"
            },
            {
              "icon": "",
              "label": "[10, 1, 2] — sort no muta el array original"
            },
            {
              "icon": "",
              "label": "[1, 10, 2] — el comparador por defecto de Array.prototype.sort convierte elementos a cadenas y los compara léxicográficamente, así que \"10\" se ordena antes de \"2\"; sort también muta el array en su lugar en lugar de devolver uno nuevo"
            },
            {
              "icon": "",
              "label": "[2, 1, 10]"
            }
          ]
        },
        {
          "question": "const user = { profile: { age: 0 } };\nconsole.log(user.profile?.age ?? 'unknown');\nconsole.log(user.address?.city ?? 'unknown');\n¿Qué imprimen las dos líneas?",
          "options": [
            {
              "icon": "",
              "label": "'unknown' luego 'unknown' — ?? trata 0 como faltante, de la misma manera que || hace"
            },
            {
              "icon": "",
              "label": "0 luego 'unknown' — ?? solo recurre a null o undefined, no a otros valores falsos como 0, así que age (0) se mantiene tal cual; user.address es undefined, así que ?. cortocircuita toda la cadena a undefined y ?? entonces suministra 'unknown'"
            },
            {
              "icon": "",
              "label": "0 luego undefined"
            },
            {
              "icon": "",
              "label": "'unknown' luego 0"
            }
          ]
        },
        {
          "question": "const obj = { a: undefined, b: function(){}, c: NaN, d: [undefined, function(){}, 1] };\nconsole.log(JSON.stringify(obj));\n¿Qué imprime esto?",
          "options": [
            {
              "icon": "",
              "label": "'{\"a\":null,\"b\":null,\"c\":null,\"d\":[null,null,1]}' — cada valor no soportado se convierte en null en todas partes, en objetos y arrays por igual"
            },
            {
              "icon": "",
              "label": "'{\"a\":undefined,\"b\":undefined,\"c\":NaN,\"d\":[undefined,null,1]}'"
            },
            {
              "icon": "",
              "label": "'{\"c\":null,\"d\":[null,null,1]}' — las propiedades de objeto cuyo valor es undefined o una función se omiten por completo, y NaN se serializa a null, pero dentro de un array esos mismos valores no soportados se convierten en null en lugar de ser descartados, porque eliminar un elemento de array cambiaría cada índice posterior"
            },
            {
              "icon": "",
              "label": "Lanza un TypeError, porque NaN no puede serializarse a JSON"
            }
          ]
        },
        {
          "question": "console.log('1');\nsetTimeout(() => console.log('2'), 0);\nPromise.resolve().then(() => console.log('3'));\nconsole.log('4');\n¿En qué orden se imprimen estos?",
          "options": [
            {
              "icon": "",
              "label": "1, 2, 3, 4"
            },
            {
              "icon": "",
              "label": "1, 4, 2, 3"
            },
            {
              "icon": "",
              "label": "1, 4, 3, 2 — el código sincrónico se ejecuta primero (así que 1 y 4 se imprimen inmediatamente), luego la cola de microtarea (callbacks de Promise) se vacía completamente antes de que la Event Loop pase al siguiente macrotask, así que 3 se imprime antes del callback setTimeout 2"
            },
            {
              "icon": "",
              "label": "1, 3, 4, 2"
            }
          ]
        },
        {
          "question": "async function foo() {\n  console.log('a');\n  await null;\n  console.log('b');\n}\nconsole.log('start');\nfoo();\nconsole.log('end');\n¿En qué orden se imprimen estos?",
          "options": [
            {
              "icon": "",
              "label": "start, a, end, b — foo() se ejecuta sincrónica hasta su primer await, así que 'a' se registra inmediatamente cuando se llama foo(); await suspende foo y devuelve el control al llamador, así que 'end' se registra antes de que se reanude 'b', que se ejecuta más tarde como una microtarea"
            },
            {
              "icon": "",
              "label": "start, a, b, end"
            },
            {
              "icon": "",
              "label": "start, end, a, b"
            },
            {
              "icon": "",
              "label": "a, start, end, b"
            }
          ]
        },
        {
          "question": "Promise.all([\n  Promise.resolve(1),\n  Promise.reject('err'),\n  Promise.resolve(3),\n]).then(r => console.log('ok', r)).catch(e => console.log('fail', e));\n¿Qué imprime esto?",
          "options": [
            {
              "icon": "",
              "label": "'fail err' — Promise.all rechaza tan pronto como cualquiera de sus promesas de entrada rechaza, ignorando los resultados de los otros, así que el manejador catch se ejecuta con la razón de esa promesa rechazada"
            },
            {
              "icon": "",
              "label": "'ok' [1, 3]"
            },
            {
              "icon": "",
              "label": "'ok' [1, undefined, 3]"
            },
            {
              "icon": "",
              "label": "Lanza sincrónicamente y ningún manejador se ejecuta nunca"
            }
          ]
        },
        {
          "question": "const fast = new Promise(res => setTimeout(() => res('fast'), 10));\nconst slow = new Promise(res => setTimeout(() => res('slow'), 100));\nPromise.race([fast, slow]).then(v => console.log(v));\n¿Qué imprime esto?",
          "options": [
            {
              "icon": "",
              "label": "'slow'"
            },
            {
              "icon": "",
              "label": "['fast', 'slow']"
            },
            {
              "icon": "",
              "label": "'fast' — Promise.race se resuelve tan pronto como la primera de sus promesas de entrada se resuelve, y el temporizador de 10ms se resuelve mucho antes del temporizador de 100ms"
            },
            {
              "icon": "",
              "label": "Lanza un error, porque race requiere exactamente dos promesas"
            }
          ]
        },
        {
          "question": "function Animal(name) { this.name = name; }\nAnimal.prototype.speak = function() { return `${this.name} makes a sound`; };\nconst dog = new Animal('Rex');\nconsole.log(dog.speak());\nconsole.log(dog.hasOwnProperty('speak'));\n¿Qué imprimen las dos líneas?",
          "options": [
            {
              "icon": "",
              "label": "'Rex makes a sound' luego true"
            },
            {
              "icon": "",
              "label": "undefined luego false"
            },
            {
              "icon": "",
              "label": "Lanza un TypeError, porque dog no tiene su propio método speak"
            },
            {
              "icon": "",
              "label": "'Rex makes a sound' luego false — speak se define en Animal.prototype, no en la instancia dog misma; dog la encuentra a través de la cadena de prototipo cuando se llama, pero hasOwnProperty solo comprueba las propias propiedades de la instancia, nada heredado"
            }
          ]
        },
        {
          "question": "class Counter {\n  #count = 0;\n  increment() { this.#count++; return this.#count; }\n}\nconst c = new Counter();\nconsole.log(c.increment());\nconsole.log(c.count);\n¿Qué imprimen las dos líneas?",
          "options": [
            {
              "icon": "",
              "label": "1 luego undefined — #count es un campo de clase privada, legible y escribible solo desde dentro de los propios métodos de la clase; c.count (sin #) es una propiedad ordinaria completamente diferente que nunca se estableció, así que es undefined"
            },
            {
              "icon": "",
              "label": "1 luego 0"
            },
            {
              "icon": "",
              "label": "1 luego 1"
            },
            {
              "icon": "",
              "label": "Lanza un SyntaxError al acceder a c.count"
            }
          ]
        },
        {
          "question": "const items = ['a', 'b'];\nconst label = `Items: ${items.length > 0 ? items.join(', ') : 'none'}`;\nconsole.log(label);\n¿Qué imprime esto?",
          "options": [
            {
              "icon": "",
              "label": "'Items: ${items.length > 0 ? items.join(\\', \\') : \\'none\\'}'"
            },
            {
              "icon": "",
              "label": "'Items: none'"
            },
            {
              "icon": "",
              "label": "'Items: a, b' — los literales de plantilla evalúan cualquier expresión dentro de ${...}, incluyendo un ternario completo, e interpolan su resultado directamente en la cadena"
            },
            {
              "icon": "",
              "label": "Lanza un SyntaxError, porque los ternarios no están permitidos dentro de literales de plantilla"
            }
          ]
        },
        {
          "question": "const nums = [1, 2, 3, 4, 5];\nconst result = nums.filter(n => n % 2 === 0).map(n => n * 10).reduce((sum, n) => sum + n, 0);\nconsole.log(result, nums);\n¿Qué imprime esto?",
          "options": [
            {
              "icon": "",
              "label": "60 [1, 2, 3, 4, 5] — filter, map y reduce cada uno devuelven un nuevo array o valor sin mutar el array original, así que nums no cambia mientras result es la suma reducida final (2 y 4, duplicados a 20 y 40, sumados a 60)"
            },
            {
              "icon": "",
              "label": "60 [2, 4, 20, 40]"
            },
            {
              "icon": "",
              "label": "150 [1, 2, 3, 4, 5]"
            },
            {
              "icon": "",
              "label": "60 [20, 40]"
            }
          ]
        }
      ],
      "optionOrderVersion": "50fc0eba818f2d72"
    }
  }
}
