
10 exercícios de JavaScript: teste seus conhecimentos
| 19/10/26Treine suas habilidades com 10 exercícios de JavaScript baseados em problemas reais do dia a dia
Treine suas habilidades com 10 exercícios de JavaScript baseados em problemas reais do dia a dia
Em resumo:
Aprender a programar em JavaScript exige mais do que horas de leitura e vídeos teóricos. É preciso colocar a mão na massa e aplicar o que você aprendeu na prática.
É pensando nisso que a Revista Quero preparou 10 exercícios sobre JavaScript que simulam situações cotidianas para quem trabalha com a tecnologia.
Pegue seu editor de código favorito e tente resolver cada etapa antes de consultar o gabarito no final do artigo.

A equipe financeira de um grande e-commerce solicitou uma função que calcule o faturamento líquido de produtos da categoria "eletronicos" que já estão com status "aprovado", aplicando ainda um desconto fixo de taxa de processamento de 5% sobre o valor de cada item válido.
JavaScript
const transacoes = [
{ id: 101, categoria: 'eletronicos', valor: 1000, status: 'aprovado' },
{ id: 102, categoria: 'eletronicos', valor: 500, status: 'pendente' },
{ id: 103, categoria: 'vestuario', valor: 200, status: 'aprovado' },
{ id: 104, categoria: 'eletronicos', valor: 2000, status: 'aprovado' }
];
Qual das implementações abaixo resolve esse problema de forma declarativa, imutável e retornando exatamente o valor esperado (2850)?
transacoes.filter(t => t.categoria === 'eletronicos' && t.status === 'aprovado').reduce((acc, t) => acc + (t.valor * 0.95));transacoes.filter(t => t.categoria === 'eletronicos' && t.status === 'aprovado').reduce((acc, t) => acc + (t.valor * 0.95), 0);transacoes.map(t => t.categoria === 'eletronicos' && t.status === 'aprovado' ? t.valor * 0.95 : 0).filter((acc, val) => acc + val);transacoes.forEach(t => { if (t.categoria === 'eletronicos' && t.status === 'aprovado') return t.valor * 0.95; });Durante a Black Friday, o servidor de busca da plataforma começou a apresentar lentidão extrema. Ao investigar os logs da aplicação front-end, o componente de barra de pesquisa dispara uma requisição HTTP (fetch) a cada evento input (ou seja, a cada letra digitada pelo usuário).
Para corrigir isso sem bibliotecas externas, você decide implementar uma função debounce, garantindo que a busca só ocorra quando o usuário fizer uma pausa de 300ms na digitação.
Qual implementação cria corretamente esse mecanismo utilizando Closures e controle de Timers?
JavaScript
function debounce(fn, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
JavaScript
function debounce(fn, delay) {
return function(...args) {
let timer = setTimeout(() => fn(...args), delay);
clearTimeout(timer);
};
}
JavaScript
function debounce(fn, delay) {
let timer = setTimeout(fn, delay);
return function(...args) {
if (timer) return;
fn(...args);
};
}
JavaScript
function debounce(fn, delay) {
return setInterval(() => {
fn();
}, delay);
}
Em uma aplicação SPA (Single Page Application) que utiliza comparação por referência (shallow comparison) para detectar mudanças de estado e atualizar a tela, um desenvolvedor júnior abriu um chamado informando que a interface não atualiza quando o usuário altera o tema do sistema de "claro" para "escuro".
Ao revisar o código dele, você encontra a seguinte estrutura de estado:
JavaScript
const estadoUsuario = {
id: 99,
nome: 'Mariana',
configuracoes: {
notificacoes: true,
aparencia: {
tema: 'claro',
fonte: 'media'
}
}
};
Sabendo que o objeto original estadoUsuario e seus sub-objetos afetados não podem sofrer mutação direta (precisam de uma nova referência em memória), qual código gera o novo estado corretamente?
const novoEstado = Object.assign(estadoUsuario, { configuracoes: { aparencia: { tema: 'escuro' } } });const novoEstado = { ...estadoUsuario }; novoEstado.configuracoes.aparencia.tema = 'escuro';const novoEstado = { ...estadoUsuario, configuracoes: { ...estadoUsuario.configuracoes, aparencia: { ...estadoUsuario.configuracoes.aparencia, tema: 'escuro' } } };const novoEstado = { ...estadoUsuario, configuracoes: { aparencia: { tema: 'escuro' } } };Você está construindo o painel inicial (Dashboard) de um sistema bancário. Ao fazer login, a tela dispara três chamadas assíncronas independentes simultaneamente: buscarSaldo(), buscarExtrato() e buscarOfertasCartao().
Atualmente, o sistema utiliza Promise.all(). O problema é que o microsserviço de ofertas de cartão cai com frequência, e quando isso acontece, o Promise.all() rejeita tudo e o cliente fica impedido de ver até mesmo o próprio saldo.
Qual abordagem resolve essa situação-problema, permitindo que as requisições rodem em paralelo e você consiga aproveitar os dados das que tiveram sucesso mesmo que uma delas falhe?
await sequenciais dentro de um único try/catch, pois assim o erro de uma promessa será ignorado automaticamente pelas demais.Promise.race([buscarSaldo(), buscarExtrato(), buscarOfertasCartao()]), que garante o retorno de todas as promessas que não deram erro primeiro.Promise.allSettled([buscarSaldo(), buscarExtrato(), buscarOfertasCartao()]) e filtrar os resultados pelo status === 'fulfilled' para extrair os valores (value).Promise.any([buscarSaldo(), buscarExtrato(), buscarOfertasCartao()]), pois ele aguarda todas finalizarem e retorna um array apenas com as bem-sucedidas.O líder de atendimento ao cliente pediu uma nova visualização em formato Kanban em que os chamados (tickets) abertos no dia sejam separados em colunas de acordo com sua prioridade ("alta", "media", "baixa").
O back-end entrega apenas um array plano de objetos. Você precisa transformar essa lista em um objeto indexado pelas prioridades em uma única passagem (O(n))
JavaScript
const chamados = [
{ protocolo: 'A1', prioridade: 'alta' },
{ protocolo: 'A2', prioridade: 'baixa' },
{ protocolo: 'A3', prioridade: 'alta' }
];
Qual alternativa realiza esse agrupamento de maneira limpa e moderna?
chamados.reduce((acc, item) => { (acc[item.prioridade] ??= []).push(item); return acc; }, {});chamados.map(item => ({ [item.prioridade]: [item] }));chamados.filter(item => item.prioridade).reduce((acc, item) => acc[item.prioridade] = item, {});Object.fromEntries(chamados.map(item => [item.prioridade, [item]]));Ao refatorar um módulo legado para usar a sintaxe de Classes do JavaScript, você criou uma classe UploadService responsável por enviar arquivos ao servidor quando o usuário clica em um botão da interface.
Porém, ao clicar no botão no navegador, o console dispara o erro: TypeError: Cannot read properties of undefined (reading 'endpoint').
JavaScript
class UploadService {
constructor() {
this.endpoint = 'https://api.empresa.com/upload';
}
enviarArquivo() {
console.log(`Enviando para ${this.endpoint}`);
}
}
const servico = new UploadService();
const botao = document.querySelector('#btn-upload');
botao.addEventListener('click', servico.enviarArquivo);
Por que esse erro ocorre nesse cenário real e qual a forma padrão de corrigi-lo?
constructor são privadas. Corrige-se declarando endpoint fora da classe como uma variável global.addEventListener altera o contexto do this dentro de enviarArquivo para referenciar o próprio elemento HTML (botao). Corrige-se passando servico.enviarArquivo.bind(servico) ou declarando enviarArquivo = () => { ... } (Arrow Function) na classe.enviarArquivo não é assíncrono (async). Corrige-se adicionando a palavra-chave async antes do nome do método.document.querySelector não suporta métodos de instâncias de classes. Corrige-se usando botao.onclick = servico.enviarArquivo().Na linha do tempo de uma rede social corporativa, o usuário faz a rolagem da página (scroll infinito) para carregar mais publicações.
Como novos posts são criados a todo segundo por outros funcionários, a paginação do banco de dados às vezes retorna na página 2 um post que já havia acabado de aparecer no fim da página 1.
Para evitar que o mesmo post apareça duplicado na tela, você precisa mesclar a lista antiga com a nova e remover objetos que possuam o mesmo id, mantendo apenas um registro por id.
Qual solução resolve esse problema com complexidade linear (sem usar loops aninhados lentos como filter com findIndex)?
const unicos = [...new Set([...postsAntigos, ...postsNovos])];const unicos = [...new Map([...postsAntigos, ...postsNovos].map(post => [post.id, post])).values()];const unicos = Object.values([...postsAntigos, ...postsNovos]);const unicos = postsAntigos.concat(postsNovos).sort((a, b) => a.id !== b.id);Sua aplicação consome dados cadastrais de uma API governamental instável. Nem todos os cidadãos possuem endereço completo ou telefone cadastrado; às vezes o nó endereco vem como null, outras vezes nem sequer existe no JSON retornado.
O código atual frequentemente quebra a aplicação inteira com erro de referência ao tentar exibir o nome da cidade:
JavaScript
// Código atual problemático:
const nomeCidade = respostaApi.dados.cidadao.endereco.cidade.nome;
Caso qualquer propriedade dessa cadeia seja null ou undefined, o sistema deve atribuir o texto "Não informada" à variável nomeCidade (incluindo o caso em que nome venha como null ou undefined, mas preservando caso fosse uma string vazia proposital "").
Qual sintaxe moderna resolve isso em uma única linha?
const nomeCidade = respostaApi.dados.cidadao.endereco.cidade.nome || 'Não informada';const nomeCidade = respostaApi?.dados?.cidadao?.endereco?.cidade?.nome ?? 'Não informada';const nomeCidade = respostaApi?.dados?.cidadao?.endereco?.cidade?.nome && 'Não informada';const nomeCidade = try { respostaApi.dados.cidadao.endereco.cidade.nome } catch { 'Não informada' };Um desenvolvedor criou uma rotina para sincronizar o estoque de uma lista de produtos consultando uma API externa para cada item.
Ele tentou usar o método forEach com async/await para aguardar cada produto ser processado antes de exibir a mensagem de conclusão:
JavaScript
async function sincronizarEstoque(idsProdutos) {
idsProdutos.forEach(async (id) => {
await atualizarProdutoNaApi(id);
});
console.log('Sincronização 100% concluída!');
}
Em produção, o log 'Sincronização 100% concluída!' aparece instantaneamente no console, antes mesmo da primeira requisição terminar, gerando inconsistência no relatório subsequente.
Por que isso acontece e como corrigir para que as requisições sejam aguardadas (em série ou em paralelo)?
forEach não aguarda o retorno de Promises passadas no seu callback. Para rodar em série (um após o outro), deve-se usar o loop for...of com await; para rodar em paralelo e aguardar todos, deve-se usar await Promise.all(idsProdutos.map(id => atualizarProdutoNaApi(id))).await dentro de uma Arrow Function é ignorado pelo motor do JavaScript. Basta trocar async (id) => por async function(id) dentro do próprio forEach.forEach só funciona de forma assíncrona se colocarmos await idsProdutos.forEach(...) no início da linha.console.log no JavaScript sempre tem prioridade na Call Stack sobre funções async, sendo obrigatório colocá-lo dentro de um setTimeout de 0ms.Em uma página de listagem de imóveis, o usuário pode aplicar vários filtros (ex: ?cidade=sp&quartos=3&mobiliado=true).
Para que o usuário possa copiar o link da página e enviar para outra pessoa no WhatsApp mantendo os filtros aplicados, sua aplicação precisa ler a Query String da URL no momento em que a página carrega e transformá-la em um objeto JavaScript simples ({ cidade: 'sp', quartos: '3', mobiliado: 'true' }) para preencher o estado inicial dos filtros.
Qual combinação nativa do JavaScript realiza essa conversão de forma direta e segura?
const filtros = JSON.parse(window.location.search);const filtros = Object.fromEntries(new URLSearchParams(window.location.search));const filtros = window.location.search.split('&').reduce((acc, item) => acc + item, {});const filtros = new Map(window.location.href);| Exercício | Alternativa Correta | Justificativa Técnica |
| 1 | B | O filter isola apenas os aprovados da categoria alvo e o reduce acumula os valores aplicando 0.95. O segundo argumento 0 no reduce é indispensável; sem ele (como na A), o acumulador iniciaria com o primeiro objeto inteiro do array, resultando em concatenação incorreta ("[object Object]1900"). |
| 2 | A | A Closure mantém a variável timer viva entre as chamadas. A cada novo evento dentro da janela de tempo, clearTimeout(timer) cancela o agendamento anterior e setTimeout reinicia a contagem de 300ms. |
| 3 | C | Em estados aninhados, é necessário clonar (...) cada nível até a propriedade alterada (shallow copy em cascata). A opção B altera a referência interna diretamente (mutação), e a D apaga as propriedades irmãs (notificacoes e fonte). |
| 4 | C | Diferente do Promise.all (que aplica fail-fast e aborta tudo se uma falhar), o Promise.allSettled aguarda todas as promessas terminarem, retornando o status individual (fulfilled ou rejected) de cada serviço. |
| 5 | A | O uso do reduce combinado com o operador Logical Nullish Assignment (??=) inicializa o array daquela chave caso ainda não exista e já insere (push) o item correspondente. A opção D sobrescreveria chaves repetidas. |
| 6 | B | Passar servico.enviarArquivo como callback desacopla o método da instância servico, fazendo o this apontar para o elemento disparador do evento (botao). O .bind(servico) ou uma Arrow Function como propriedade de classe trava o this léxico na instância correta. |
| 7 | B | Um Set (opção A) só remove duplicatas de tipos primitivos ou se os objetos tiverem exatamente a mesma referência de memória. Já o Map indexado por [post.id, post] sobrescreve chaves de mesmo id em tempo $O(n)$, e .values() extrai os objetos únicos. |
| 8 | B | O Optional Chaining (?.) evita o erro TypeError retornando undefined caso algum elo da corrente não exista. O Nullish Coalescing (??) retorna o valor padrão apenas se a expressão à esquerda for null ou undefined. |
| 9 | A | O método forEach apenas dispara as funções de callback em um loop síncrono sem observar as Promises retornadas. Para controle de fluxo assíncrono, utiliza-se for...of (execução sequencial) ou map com Promise.all (execução concorrente). |
| 10 | B | new URLSearchParams(window.location.search) faz o parse seguro dos parâmetros da URL (lidando inclusive com caracteres especiais codificados), e Object.fromEntries() converte o iterador de pares [chave, valor] diretamente em um objeto literal. |
Se você quer aprender JavaScript do zero ao avançado, com projetos reais e suporte de carreira, conte com a Allevo Tech.
Na plataforma, você encontra trilhas de aprendizado personalizadas e mentorias com especialistas na área que vão te levar do zero ao domínio absoluto da programação.
Além disso, só na Allevo Tech, você garante certificações a cada novo módulo concluído, garantindo que os seus conhecimentos sejam reconhecidos no mercado de trabalho.
Clique no botão abaixo e conheça as formações da Allevo Tech que podem impulsionar a sua trajetória profissional.


Treine suas habilidades com 10 exercícios de JavaScript baseados em problemas reais do dia a dia

Conheça certificações online em Software, Dados e Inteligência Artificial que podem ajudar a desenvolver habilidades e fortalecer a carreira em TI.

Certificações em Dados podem fortalecer o currículo e comprovar conhecimentos técnicos. Entenda como elas são avaliadas pelos recrutadores e como combiná-las com experiência prática.