{
  "assessmentTests": {
    "javascript_test": {
      "name": "Teste de JavaScript",
      "desc": "30 questões baseadas em cenários sobre sintaxe principal e coerção de tipos, funções/escopo/closures e this, objetos/arrays e estruturas de dados, e event loop/async/idiomas de prototipo — descubra se suas habilidades em JavaScript correspondem ao que as ofertas de emprego significam por proficiência em JavaScript.",
      "recommendation": "Seu perfil de habilidades em JavaScript",
      "results": {
        "beginner": {
          "name": "Iniciante",
          "desc": "Você pode escrever funções e objetos funcionais, mas as questões que você perdeu se agrupam em torno de coerção de tipos e semântica de referência em vez de sintaxe — == convertendo silenciosamente tipos antes de comparar, uma atribuição de array que compartilha um objeto em vez de copiá-lo, por que NaN nunca é igual a si mesmo. Isto não é sobre ser ruim em JavaScript; estas são as regras específicas que confundem pessoas que aprenderam a linguagem escrevendo código que casualmente funcionava em vez de aprender como valores e referências realmente se comportam. Eles importam no trabalho porque cada um é um lugar onde o código é executado sem um erro e ainda faz a coisa errada.",
          "recommendation": "Comece com três coisas, nesta ordem: por que == coerce tipos antes de comparar, enquanto === não o faz, por que atribuir um array ou objeto a uma nova variável copia a referência em vez do valor, e como var é elevado e inicializado para undefined enquanto let e const permanecem na zona morta temporal até que sua declaração seja executada. O guia de JavaScript do MDN abrange todos os três com exemplos executáveis."
        },
        "intermediate": {
          "name": "Intermediário",
          "desc": "Você lida confortavelmente com código de aplicativo cotidiano — funções, métodos de array, fluxo de controle direto — e não seria retardado pelo trabalho de recursos rotineiros. A lacuna entre aqui e Avançado é principalmente o que acontece quando as regras de escopo e temporização de JavaScript interagem: um var capturado dentro dos callbacks de um loop, todos lendo o mesmo valor final, um this que silenciosamente para de apontar para o objeto uma vez que um método é desanexado dele, uma cópia spread que parece profunda, mas copia apenas um nível. Estes são o tipo de bugs que passam por uma leitura rápida e só aparecem sob uma condição de tempo de execução específica.",
          "recommendation": "Concentre-se em como escopo e this realmente se resolvem na hora da chamada em vez de na hora da definição: por que um loop for baseado em var entrega a cada callback o mesmo valor final do contador enquanto let não o faz, como this é determinado por como uma função é chamada em vez de onde é escrita, e por que um spread ou Object.assign copia apenas o nível superior de um objeto. Depois o event loop, já que é isso que confunde pessoas que já conhecem promises individualmente."
        },
        "advanced": {
          "name": "Avançado",
          "desc": "Este é o nível que a maioria das ofertas de emprego significa por \"JavaScript forte\". Você lê uma cadeia de filter/map/reduce e pode prever exatamente o que ela retorna sem mutar o array original, você sabe por que um console.log síncrono sempre vence uma microtask que sempre vence um setTimeout, e você vai atrás de Promise.all sabendo que ele rejeita no instante em que qualquer promise rejeita em vez de esperar pelo resto. O que separa esta banda do topo é o lado do protótipo e do modelo de objeto do trabalho: como a cadeia de protótipos resolve um método herdado versus hasOwnProperty, e o que um campo de classe privado realmente oculta.",
          "recommendation": "Mergulhe nas partes que protegem o estado que outro código também toca: como a cadeia de protótipos JavaScript resolve lookups de método que não estão na própria instância, o que os #fields privados de uma classe realmente ocultam do acesso externo, e como JSON.stringify trata undefined e funções de forma diferente dentro de um objeto simples versus dentro de um array. O material do MDN sobre o event loop e a cadeia de protótipos é o próximo passo natural para ambos."
        },
        "expert": {
          "name": "Especialista",
          "desc": "Você conquistou o topo de cada seção — sintaxe principal, tipos e coerção; funções, escopo, closures e this; objetos, arrays e estruturas de dados; e async, event loop, protótipos e idiomas. Praticamente, isto significa que você pode receber a função de alguém e explicar por que ela retorna o que retorna, não apenas o que a sintaxe diz que deveria fazer, que é a habilidade mais difícil e mais valiosa. Neste nível, a própria linguagem raramente é o fator limitante; o limite é geralmente fluxo de controle assíncrono ou a forma dos dados abaixo disso.",
          "recommendation": "Os retornos estão agora em design e diagnóstico: raciocinar sobre uma condição de corrida entre Promise.all e Promise.race em vez de recitar suas definições, escolher quando um closure é a ferramenta certa para estado privado versus quando um campo privado de classe é, e ler um bug de ordenação microtask-versus-macrotask em logs de produção em vez de em um quiz. Se estiver sendo testado para um papel, descreva um bug de timing que encontrou — uma variável de closure obsoleta ou um callback assíncrono fora de ordem — em vez de nomear recursos JavaScript; isto demonstra raciocínio, não apenas vocabulário."
        }
      },
      "questions": [
        {
          "question": "console.log(x);\nvar x = 5;\nO que é impresso?",
          "options": [
            {"icon": "","label": "5 — as declarações var são executadas imediatamente, antes de qualquer outro código"},
            {"icon": "","label": "ReferenceError: x is not defined"},
            {"icon": "","label": "undefined — as declarações var são elevadas ao topo de seu escopo e inicializadas para undefined, mas a atribuição é executada no local, então antes de executar a variável existe, mas contém undefined"},
            {"icon": "","label": "null"}
          ]
        },
        {
          "question": "console.log(y);\nlet y = 5;\nO que acontece?",
          "options": [
            {"icon": "","label": "Lança ReferenceError: Cannot access 'y' before initialization — let é elevado ao topo de seu escopo, mas permanece na zona morta temporal até que a linha de declaração seja realmente executada"},
            {"icon": "","label": "Imprime undefined, o mesmo que var faria"},
            {"icon": "","label": "Imprime 5"},
            {"icon": "","label": "Lança um SyntaxError no tempo de análise"}
          ]
        },
        {
          "question": "const arr = [1, 2, 3];\narr.push(4);\nconsole.log(arr.length);\nO que é impresso?",
          "options": [
            {"icon": "","label": "4 — const apenas previne a reatribuição da ligação arr em si, não a mutação do objeto array para o qual aponta, então push ainda funciona"},
            {"icon": "","label": "Lança um TypeError, porque arr é const"},
            {"icon": "","label": "3 — push é bloqueado em um array const"},
            {"icon": "","label": "undefined"}
          ]
        },
        {
          "question": "console.log('' == 0);\nconsole.log('' === 0);\nO que essas duas linhas imprimem?",
          "options": [
            {"icon": "","label": "true depois true"},
            {"icon": "","label": "false depois false"},
            {"icon": "","label": "true depois false — == coerça a string vazia para 0 antes de comparar, enquanto === compara tipo e valor sem coerção, e uma string nunca é estritamente igual a um número"},
            {"icon": "","label": "false depois true"}
          ]
        },
        {
          "question": "console.log(NaN === NaN);\nconsole.log(Number.isNaN(NaN));\nO que essas duas linhas imprimem?",
          "options": [
            {"icon": "","label": "true depois true"},
            {"icon": "","label": "true depois false"},
            {"icon": "","label": "false depois false"},
            {"icon": "","label": "false depois true — NaN é o único valor em JavaScript que não é igual a si mesmo sob ===, mas Number.isNaN() o detecta corretamente de qualquer forma"}
          ]
        },
        {
          "question": "console.log(typeof null);\nO que é impresso?",
          "options": [
            {"icon": "","label": "\"null\""},
            {"icon": "","label": "\"object\" — um bug de longa data na linguagem: typeof null retorna \"object\", embora null seja um primitivo, e foi mantido para compatibilidade com versões anteriores desde então"},
            {"icon": "","label": "\"undefined\""},
            {"icon": "","label": "\"boolean\""}
          ]
        },
        {
          "question": "Qual dos seguintes é verdadeiro quando usado em uma condição if?",
          "options": [
            {"icon": "","label": "0"},
            {"icon": "","label": "\"\" (uma string vazia)"},
            {"icon": "","label": "null"},
            {"icon": "","label": "\"0\" — uma string não vazia é sempre verdadeira em JavaScript, mesmo quando seu conteúdo é a própria string \"0\""}
          ]
        },
        {
          "question": "foo();\nfunction foo() { console.log('a'); }\nbar();\nvar bar = function() { console.log('b'); };\nO que acontece?",
          "options": [
            {"icon": "","label": "Ambas as chamadas imprimem, \"a\" depois \"b\""},
            {"icon": "","label": "foo() imprime \"a\"; bar() lança TypeError: bar is not a function — declarações de função são totalmente elevadas com seu corpo, mas uma expressão de função atribuída a var apenas eleva a própria var (como undefined), não a atribuição"},
            {"icon": "","label": "Ambas as chamadas lançam ReferenceError"},
            {"icon": "","label": "foo() lança; 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());\nO que essas duas linhas imprimem?",
          "options": [
            {"icon": "","label": "1 depois 1"},
            {"icon": "","label": "0 depois 1"},
            {"icon": "","label": "undefined depois undefined"},
            {"icon": "","label": "1 depois 2 — a função retornada fecha sobre a mesma variável count do escopo do makeCounter, e essa variável persiste entre chamadas em vez de ser redefinida"}
          ]
        },
        {
          "question": "for (var i = 0; i < 3; i++) {\n  setTimeout(() => console.log(i), 0);\n}\nO que é impresso?",
          "options": [
            {"icon": "","label": "0, 1, 2"},
            {"icon": "","label": "3, 3, 3 — var tem escopo de função, não de bloco, portanto, todos os três callbacks fecham sobre o mesmo i, que já completou o loop e é igual a 3 quando qualquer um deles realmente executa"},
            {"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}\nO que é impresso?",
          "options": [
            {"icon": "","label": "3, 3, 3"},
            {"icon": "","label": "0, 0, 0"},
            {"icon": "","label": "undefined, undefined, undefined"},
            {"icon": "","label": "0, 1, 2 — let cria uma ligação fresca de i para cada iteração do loop, portanto, cada callback fecha sobre sua própria cópia separada em vez de uma variável compartilhada"}
          ]
        },
        {
          "question": "function greet() { return `Hi, ${this.name}`; }\nconst person = { name: 'Ana' };\nconsole.log(greet.call(person));\nO que é impresso?",
          "options": [
            {"icon": "","label": "\"Hi, Ana\" — Function.prototype.call invoca a função com this explicitamente definido para seu primeiro argumento, portanto, this.name se resolve em relação a person"},
            {"icon": "","label": "\"Hi, undefined\" — call() funciona apenas em funções que já são métodos de um objeto"},
            {"icon": "","label": "Lança um TypeError, porque greet não é um método de person"},
            {"icon": "","label": "\"Hi, ${this.name}\" — call() não avalia o template literal"}
          ]
        },
        {
          "question": "const timer = {\n  seconds: 0,\n  start: function() {\n    setInterval(() => { this.seconds++; }, 1000);\n  },\n};\nPor que as pessoas preferem a função de seta sobre function() { this.seconds++ } dentro de setInterval aqui?",
          "options": [
            {"icon": "","label": "As funções de seta não têm seu próprio this — elas capturam this lexicamente da função start envolvente, que é timer (porque start foi chamado como timer.start()), portanto, this.seconds se refere corretamente a timer.seconds a cada tick"},
            {"icon": "","label": "As funções de seta executam mais rápido do que funções regulares no tempo de execução"},
            {"icon": "","label": "As funções de seta vinculam automaticamente this ao objeto global, que por acaso é igual a timer"},
            {"icon": "","label": "As funções de seta convertem automaticamente this em uma variável global"}
          ]
        },
        {
          "question": "function multiply(a, b) { return a * b; }\nconst double = multiply.bind(null, 2);\nconsole.log(double(5));\nO que é impresso?",
          "options": [
            {"icon": "","label": "Lança um erro, porque bind requer ambos os argumentos antecipadamente"},
            {"icon": "","label": "10 — bind retorna uma nova função com this e quaisquer argumentos iniciais pré-definidos; chamar double(5) fornece o argumento restante b=5, portanto, multiply(2, 5) é executado"},
            {"icon": "","label": "NaN"},
            {"icon": "","label": "7, porque bind adiciona seus argumentos à chamada em vez de substituí-los"}
          ]
        },
        {
          "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();\nO que essas duas chamadas console.log imprimem?",
          "options": [
            {"icon": "","label": "20 depois 20"},
            {"icon": "","label": "20 depois 10 — o let x próprio de inner obscurece o x de outer apenas dentro do escopo de inner; o x próprio de outer nunca é tocado"},
            {"icon": "","label": "10 depois 10"},
            {"icon": "","label": "Lança um ReferenceError, porque x é declarado duas vezes"}
          ]
        },
        {
          "question": "const a = [1, 2, 3];\nconst b = a;\nb.push(4);\nconsole.log(a.length);\nO que é impresso?",
          "options": [
            {"icon": "","label": "3 — b é uma cópia separada de a"},
            {"icon": "","label": "undefined"},
            {"icon": "","label": "Lança um TypeError, porque a é const"},
            {"icon": "","label": "4 — arrays são objetos, portanto, const b = a copia a referência, não o array; mutar b através de push também muta o array exato para o qual a aponta"}
          ]
        },
        {
          "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);\nO que é impresso?",
          "options": [
            {"icon": "","label": "'Ana' 2 — spread faz uma cópia profunda de cada objeto e array aninhados"},
            {"icon": "","label": "'Ana' 3 — spread é apenas uma cópia superficial: uma propriedade primitiva como name é copiada por valor, portanto, reatribuir copy.name nunca toca original, mas um array aninhado como tags é copiado por referência, portanto, fazer push para copy.tags também muta o array exato para o qual original.tags aponta"},
            {"icon": "","label": "'Bea' 3 — spread copia tudo por referência, portanto, tanto as alterações de nível superior quanto as alterações aninhadas voltam ao original"},
            {"icon": "","label": "'Bea' 2"}
          ]
        },
        {
          "question": "const { a: x = 10, b: y = 20 } = { a: 5 };\nconsole.log(x, y);\nO que é impresso?",
          "options": [
            {"icon": "","label": "undefined undefined"},
            {"icon": "","label": "5 undefined"},
            {"icon": "","label": "10 20"},
            {"icon": "","label": "5 20 — a é renomeado para x e está presente, portanto mantém seu valor 5; b é renomeado para y e está ausente, portanto volta para seu padrão 20"}
          ]
        },
        {
          "question": "const [first, ...rest] = [1, 2, 3, 4];\nconsole.log(rest);\nO que é impresso?",
          "options": [
            {"icon": "","label": "[1, 2, 3, 4]"},
            {"icon": "","label": "[2, 3, 4] — rest coleta cada elemento restante após first em um novo array"},
            {"icon": "","label": "2"},
            {"icon": "","label": "[1]"}
          ]
        },
        {
          "question": "const nums = [10, 1, 2];\nnums.sort();\nconsole.log(nums);\nO que é impresso?",
          "options": [
            {"icon": "","label": "[1, 2, 10] — sort sempre ordena números numericamente em ordem crescente por padrão"},
            {"icon": "","label": "[10, 1, 2] — sort não muta o array original"},
            {"icon": "","label": "[1, 10, 2] — o comparador padrão de Array.prototype.sort converte elementos em strings e os compara lexicograficamente, portanto, \"10\" é classificado antes de \"2\"; sort também muta o array no local em vez de retornar um novo"},
            {"icon": "","label": "[2, 1, 10]"}
          ]
        },
        {
          "question": "const user = { profile: { age: 0 } };\nconsole.log(user.profile?.age ?? 'unknown');\nconsole.log(user.address?.city ?? 'unknown');\nO que essas duas linhas imprimem?",
          "options": [
            {"icon": "","label": "'unknown' depois 'unknown' — ?? trata 0 como ausente, da mesma forma que || faz"},
            {"icon": "","label": "0 depois 'unknown' — ?? volta apenas para null ou undefined, não para outros valores falsos como 0, portanto age (0) é mantido como está; user.address é undefined, portanto ?. interrompe toda a cadeia para undefined e ?? então fornece 'unknown'"},
            {"icon": "","label": "0 depois undefined"},
            {"icon": "","label": "'unknown' depois 0"}
          ]
        },
        {
          "question": "const obj = { a: undefined, b: function(){}, c: NaN, d: [undefined, function(){}, 1] };\nconsole.log(JSON.stringify(obj));\nO que é impresso?",
          "options": [
            {"icon": "","label": "'{\"a\":null,\"b\":null,\"c\":null,\"d\":[null,null,1]}' — todo valor não suportado se torna null em todos os lugares, em objetos e arrays"},
            {"icon": "","label": "'{\"a\":undefined,\"b\":undefined,\"c\":NaN,\"d\":[undefined,null,1]}'"},
            {"icon": "","label": "'{\"c\":null,\"d\":[null,null,1]}' — as propriedades do objeto cujo valor é undefined ou uma função são completamente omitidas, e NaN é serializado para null, mas dentro de um array esses mesmos valores não suportados se tornam null em vez de serem removidos, porque remover um elemento do array mudaria cada índice posterior"},
            {"icon": "","label": "Lança um TypeError, porque NaN não pode ser serializado para JSON"}
          ]
        },
        {
          "question": "console.log('1');\nsetTimeout(() => console.log('2'), 0);\nPromise.resolve().then(() => console.log('3'));\nconsole.log('4');\nEm que ordem são impressas?",
          "options": [
            {"icon": "","label": "1, 2, 3, 4"},
            {"icon": "","label": "1, 4, 2, 3"},
            {"icon": "","label": "1, 4, 3, 2 — o código síncrono é executado primeiro (portanto, 1 e 4 são impressos imediatamente), depois a fila microtask é completamente drenada antes do event loop passar para a próxima macrotask, portanto, 3 é impresso antes do 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');\nEm que ordem são impressas?",
          "options": [
            {"icon": "","label": "start, a, end, b — foo() é executado sincronamente até seu primeiro await, portanto 'a' é impresso imediatamente quando foo() é chamado; await então suspende foo e retorna o controle ao chamador, portanto 'end' é impresso antes do 'b' retomado, que é executado posteriormente como uma microtask"},
            {"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));\nO que é impresso?",
          "options": [
            {"icon": "","label": "'fail err' — Promise.all rejeita assim que qualquer uma de suas promises de entrada rejeita, ignorando os resultados dos outros, portanto o manipulador catch é executado com o motivo dessa rejeição"},
            {"icon": "","label": "'ok' [1, 3]"},
            {"icon": "","label": "'ok' [1, undefined, 3]"},
            {"icon": "","label": "Lança sincronamente e nenhum manipulador nunca é executado"}
          ]
        },
        {
          "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));\nO que é impresso?",
          "options": [
            {"icon": "","label": "'slow'"},
            {"icon": "","label": "['fast', 'slow']"},
            {"icon": "","label": "'fast' — Promise.race se resolve assim que a primeira de suas promises de entrada se resolve, e o timer de 10ms é resolvido bem antes do de 100ms"},
            {"icon": "","label": "Lança um erro, porque race requer exatamente duas promises"}
          ]
        },
        {
          "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'));\nO que essas duas linhas imprimem?",
          "options": [
            {"icon": "","label": "'Rex makes a sound' depois true"},
            {"icon": "","label": "undefined depois false"},
            {"icon": "","label": "Lança um TypeError, porque dog não tem seu próprio método speak"},
            {"icon": "","label": "'Rex makes a sound' depois false — speak é definido no Animal.prototype, não na própria instância dog; dog a encontra através da cadeia de protótipos quando é chamada, mas hasOwnProperty verifica apenas as propriedades próprias da instância, não nada herdado"}
          ]
        },
        {
          "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);\nO que essas duas linhas imprimem?",
          "options": [
            {"icon": "","label": "1 depois undefined — #count é um campo de classe privado, legível e escrevível apenas de dentro dos próprios métodos da classe; c.count (sem #) é uma propriedade completamente diferente e comum que nunca foi definida, portanto é undefined"},
            {"icon": "","label": "1 depois 0"},
            {"icon": "","label": "1 depois 1"},
            {"icon": "","label": "Lança um SyntaxError ao acessar c.count"}
          ]
        },
        {
          "question": "const items = ['a', 'b'];\nconst label = `Items: ${items.length > 0 ? items.join(', ') : 'none'}`;\nconsole.log(label);\nO que é impresso?",
          "options": [
            {"icon": "","label": "'Items: ${items.length > 0 ? items.join(\\', \\') : \\'none\\'}'"},
            {"icon": "","label": "'Items: none'"},
            {"icon": "","label": "'Items: a, b' — template literals avaliam qualquer expressão colocada dentro de ${...}, incluindo um ternário completo, e interpolam seu resultado diretamente na string"},
            {"icon": "","label": "Lança um SyntaxError, porque ternários não são permitidos dentro de template literals"}
          ]
        },
        {
          "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);\nO que é impresso?",
          "options": [
            {"icon": "","label": "60 [1, 2, 3, 4, 5] — filter, map e reduce cada um retorna um novo array ou valor sem mutar o array original, portanto nums não é alterado enquanto result é a soma reduzida final (2 e 4, duplicados para 20 e 40, somados para 60)"},
            {"icon": "","label": "60 [2, 4, 20, 40]"},
            {"icon": "","label": "150 [1, 2, 3, 4, 5]"},
            {"icon": "","label": "60 [20, 40]"}
          ]
        }
      ],
      "optionOrderVersion": "50fc0eba818f2d72"
    }
  }
}
