Principais erros de desenvolvedores JavaScript iniciantes: nível avançado
Quando você já domina a sintaxe básica do JavaScript e escreveu alguns scripts para o navegador ou Node.js, parece que o mais difícil já passou. Mas é exatamente na fase de transição para projetos reais (muitas vezes no lado do servidor com Node.js) que os desenvolvedores se deparam com erros traiçoeiros que não quebram o código imediatamente, mas levam a bugs difíceis de detectar, vazamentos de memória e comportamentos inesperados. Neste artigo, vamos analisar os 4 principais erros avançados que desenvolvedores JavaScript iniciantes cometem e mostrar como evitá-los.
1. Falta de compreensão do funcionamento de closures em loops (Closures in Loops)
Um problema clássico que migrou do JavaScript do navegador para o Node.js. Muitos sabem que var tem escopo de função, e não de bloco, mas o erro ocorre até mesmo com let quando callbacks assíncronos são usados incorretamente.
Problema: Você cria um array de funções em um loop e espera que cada função lembre seu próprio valor do contador. Em vez disso, todas as funções referenciam o último valor.
// ❌ Erro: todas as chamadas retornarão 3const functions = [];for (var i = 0; i < 3; i++) { functions.push(() => console.log(i));}functions.forEach(fn => fn()); // 3, 3, 3Por que isso acontece? O closure captura não o valor da variável i, mas uma referência à própria variável. No momento em que a função é chamada, o loop já terminou e i é igual a 3.
Solução (para Node.js): Use let para escopo de bloco ou crie um novo closure através de IIFE (Immediately Invoked Function Expression).
// ✅ Correção com letconst functions = [];for (let i = 0; i < 3; i++) { functions.push(() => console.log(i));}functions.forEach(fn => fn()); // 0, 1, 2
// ✅ Correção com closure (immediately invoked function)for (var i = 0; i < 3; i++) { (function(j) { functions.push(() => console.log(j)); })(i);}2. Ignorar a assincronicidade: "Corrida" de promises e perda de contexto this
Node.js é um ambiente assíncrono. Um dos erros mais comuns é tentar usar o resultado de uma operação assíncrona antes de sua conclusão ou perder o contexto this dentro de métodos.
Exemplo 1: Condição de corrida (Race condition)
// ❌ Erro: o resultado pode ser undefinedlet userData;fetchUserData().then(data => { userData = data;});console.log(userData); // undefined — o código síncrono foi executado antesExemplo 2: Perda de this no callback
class Logger { constructor(prefix) { this.prefix = prefix; } logLater() { setTimeout(function() { console.log(this.prefix); // undefined — this referencia o objeto global }, 100); }}const logger = new Logger('INFO:');logger.logLater(); // undefinedSolução: Sempre use async/await para operações sequenciais e arrow functions para preservar o contexto.
// ✅ Correção com async/awaitasync function getUserData() { const userData = await fetchUserData(); console.log(userData); // agora os dados estão garantidos}
// ✅ Correção com arrow functionclass Logger { constructor(prefix) { this.prefix = prefix; } logLater() { setTimeout(() => { console.log(this.prefix); // INFO: }, 100); }}3. Tratamento incorreto de erros em cadeias de promises e streams
No Node.js, muitas operações retornam promises ou streams. Desenvolvedores iniciantes frequentemente esquecem de tratar erros no meio da cadeia ou em streams, o que leva a falhas silenciosas