Debugging eficiente
Esta aula ensina técnicas profissionais de debugging em JavaScript, indo além do console.log, com uso de breakpoints, análise de stack traces e a instrução debugger, para resolver bugs de forma mais rápida e eficiente.
Bem-vindo à aula sobre debugging eficiente em JavaScript. Se você já passou horas adicionando console.log para tentar entender o que seu código está fazendo, esta aula é para você. Vamos explorar ferramentas e técnicas que vão transformar a maneira como você encontra e corrige erros, tornando o processo mais rápido e menos frustrante.
O debugging é uma habilidade essencial para qualquer desenvolvedor. Não se trata apenas de encontrar o erro, mas de entender o fluxo do programa, o estado das variáveis e o porquê de cada comportamento. Com as técnicas certas, você vai economizar tempo e escrever código mais confiável.
console além do log
O console.log é o primeiro recurso que aprendemos, mas o objeto console oferece muito mais. Métodos como console.table, console.group, console.time e console.assert podem fornecer informações mais claras e organizadas sobre o estado do seu código.
Por exemplo, console.table exibe arrays e objetos em uma tabela legível, facilitando a visualização de grandes conjuntos de dados. console.group permite agrupar mensagens relacionadas, e console.time mede o tempo de execução de trechos de código, útil para identificar gargalos de performance.
// Exemplo de console.table
const usuarios = [
{ nome: 'Ana', idade: 30 },
{ nome: 'Bruno', idade: 25 },
{ nome: 'Carla', idade: 28 }
];
console.table(usuarios);
// Exemplo de console.group
console.group('Validação de formulário');
console.log('Campo nome: preenchido');
console.log('Campo email: inválido');
console.groupEnd();
// Exemplo de console.time
console.time('Loop');
for (let i = 0; i < 1000000; i++) {}
console.timeEnd('Loop');
// Exemplo de console.assert
const valor = 10;
console.assert(valor > 5, 'Valor deveria ser maior que 5');Esses métodos ajudam a obter informações mais específicas e organizadas, reduzindo a poluição visual do console e facilitando a análise.
Breakpoints no DevTools
Os breakpoints são a base do debugging interativo. Em vez de adicionar logs e reexecutar o código, você pode pausar a execução em uma linha específica e inspecionar o estado do programa naquele momento. O DevTools do navegador oferece várias formas de definir breakpoints: por linha de código, por evento, por chamada de função, entre outros.
Para definir um breakpoint por linha, basta clicar no número da linha no painel Sources. Quando a execução atingir essa linha, o código pausa e você pode usar o painel de escopo (Scope) para ver todas as variáveis locais e globais, além de poder executar código no console enquanto a execução está pausada.
function calcularTotal(preco, quantidade) {
const subtotal = preco * quantidade; // Defina um breakpoint aqui
const desconto = subtotal * 0.1;
return subtotal - desconto;
}
const total = calcularTotal(100, 3);
console.log(total);Além dos breakpoints por linha, você pode usar breakpoints condicionais, que pausam apenas quando uma condição é verdadeira, ou breakpoints de evento, que disparam quando ocorre um evento específico, como um clique ou uma chamada de rede. Essas opções são poderosas para isolar problemas em cenários complexos.
Stack traces
Quando um erro ocorre, o console exibe uma stack trace, que mostra a sequência de chamadas de função que levaram ao erro. Entender como ler essa pilha é crucial para identificar a origem do problema, especialmente em aplicações com muitas funções aninhadas.
A stack trace lista cada chamada de função, da mais recente para a mais antiga, com o nome da função, o arquivo e o número da linha. Ao examinar a pilha, você pode rastrear o caminho percorrido até o erro, localizando a chamada que causou o problema.
function funcaoA() {
throw new Error('Erro na função A');
}
function funcaoB() {
funcaoA();
}
function funcaoC() {
funcaoB();
}
funcaoC();O erro acima gerará uma stack trace como: funcaoA em script.js:3, funcaoB em script.js:8, funcaoC em script.js:13. Isso mostra o fluxo exato da chamada, permitindo que você vá diretamente ao ponto de origem.
debugger
A instrução debugger é uma forma de criar um breakpoint programático no código. Quando o interpretador encontra essa instrução, ele pausa a execução automaticamente, desde que as ferramentas de desenvolvimento estejam abertas. Isso é útil quando você quer pausar em um ponto específico sem precisar clicar no DevTools, especialmente em código que é executado dinamicamente.
Para usar, basta inserir a palavra debugger na linha desejada. Ao executar o script com o DevTools aberto, a execução pausará ali, permitindo que você inspecione variáveis e continue a execução passo a passo.
function processarDados(dados) {
debugger; // A execução pausa aqui
// processar dados...
return dados.map(item => item * 2);
}
const resultado = processarDados([1, 2, 3]);
console.log(resultado);É importante lembrar de remover as instruções debugger do código de produção, pois podem causar pausas indesejadas. Uma prática comum é usar um build system para filtrá-las em ambientes de produção.
Boas práticas de debugging
Para se tornar um debugger eficiente, adote boas práticas: use nomes de variáveis descritivos, que facilitam a leitura do estado; escreva funções pequenas e com responsabilidade única, que ajudam a isolar bugs; e faça uso de testes automatizados para evitar regressões. Além disso, aprenda a usar os atalhos do DevTools, como F10 para passar por cima de uma função e F11 para entrar nela.
Outra dica é não tentar corrigir o bug imediatamente. Primeiro, entenda completamente o problema: reproduza-o, analise as causas e só então implemente a correção. Documente o que aprendeu para evitar erros semelhantes no futuro.
Referências
- MDN: Console API
- MDN: Ferramentas de desenvolvimento do navegador
- Chrome DevTools: JavaScript Debugging
- MDN: debugger statement
- MDN: Error.prototype.stack
- Firefox DevTools: Debugger
Exercícios
- Exercício 1: Crie um script que use
console.tablepara exibir uma lista de estudantes com nome e nota. Em seguida, useconsole.timeeconsole.timeEndpara medir o tempo de execução de um loop que percorre essa lista. - Exercício 2: Escreva uma função que recebe um array de números e retorna a soma. Use um breakpoint condicional para pausar apenas quando o valor do elemento for maior que 10. Explique como você configuraria esse breakpoint no DevTools.
- Exercício 3: Escreva um código que cause um erro e, em seguida, analise a stack trace no console. Identifique a origem do erro e explique o que cada linha da stack trace indica.
- Exercício 4: Crie uma função que usa a instrução
debuggere explique quando e por que você usaria isso em vez de um breakpoint manual no DevTools. - Exercício 5: Liste três boas práticas de debugging e explique como cada uma pode ajudar a resolver problemas mais rapidamente.
const estudantes = [
{ nome: 'Maria', nota: 8.5 },
{ nome: 'João', nota: 7.0 },
{ nome: 'Pedro', nota: 9.2 }
];
console.table(estudantes);
console.time('percorrer');
for (let i = 0; i < estudantes.length; i++) {
console.log(estudantes[i].nome);
}
console.timeEnd('percorrer');elemento > 10. O código pausará apenas quando essa condição for verdadeira.function somaArray(arr) {
let soma = 0;
for (let i = 0; i < arr.length; i++) {
const elemento = arr[i]; // Breakpoint condicional aqui: elemento > 10
soma += elemento;
}
return soma;
}
const numeros = [5, 12, 8, 15];
console.log(somaArray(numeros));function funcaoA() {
throw new Error('Erro aqui');
}
function funcaoB() {
funcaoA();
}
function funcaoC() {
funcaoB();
}
funcaoC();A stack trace mostrará as chamadas em ordem inversa: primeiro funcaoA (onde o erro foi lançado), depois funcaoB (que chamou funcaoA), e funcaoC (que chamou funcaoB). Isso indica o caminho percorrido até o erro, permitindo identificar a origem.function calcular(valor) {
debugger; // pausa automática
return valor * 2;
}
console.log(calcular(5));A instrução debugger é útil quando você está depurando código que não é facilmente acessível via DevTools, como código dinâmico ou gerado. Ela também pode ser usada temporariamente durante o desenvolvimento para forçar uma pausa sem precisar procurar a linha no editor. No entanto, deve ser removida em produção.