As Lacunas de JavaScript que Todo Dev Ignora (e Paga Caro Depois)
## O Problema Real
Você sabe escrever JavaScript. Faz componentes React, consome APIs, usa npm. Mas quando algo quebra de verdade, você congela.
Isso acontece porque existem **lacunas fundamentais** que tutoriais de YouTube nunca cobrem. São conceitos que parecem "teóricos demais" até o dia em que um bug em produção te faz perder 8 horas.
Vou cobrir cada uma dessas lacunas. Com código real. Sem enrolação.
## Lacuna 1: Coerção de Tipos
JavaScript é fracamente tipado. Isso significa que o motor faz conversões implícitas o tempo todo. A maioria dos devs decora que `==` é ruim e `===` é bom. Mas não entende **por quê**.
### O que realmente acontece
Quando você usa `==`, o JavaScript segue o algoritmo Abstract Equality Comparison da spec ECMAScript. Não é aleatório. Tem regras.
```javascript
// Coerção em ação
console.log(1 == "1"); // true — string vira number
console.log(0 == false); // true — boolean vira number (false → 0)
console.log("" == false); // true — ambos viram 0
console.log(null == undefined); // true — caso especial da spec
console.log(null == 0); // false — null só é == a undefined
// O clássico que quebra lógica de formulário
const input = document.getElementById("quantity").value; // "0"
if (input) {
console.log("tem valor!"); // Executa! "0" é truthy como string
}
if (Number(input)) {
console.log("tem quantidade!"); // NÃO executa. 0 é falsy.
}
A armadilha do typeof
console.log(typeof null); // "object" — bug histórico, nunca corrigido
console.log(typeof undefined); // "undefined"
console.log(typeof NaN); // "number" — sim, Not-a-Number é number
console.log(NaN === NaN); // false — NaN não é igual a si mesmo
// Como verificar NaN corretamente
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN("hello")); // false (correto!)
console.log(isNaN("hello")); // true (bugado — converte antes)
Regra prática: use === sempre. Use Number(), String() ou Boolean() para conversões explícitas. Nunca confie em coerção implícita em lógica de negócio.
Lacuna 2: Closures de Verdade
Todo mundo "sabe" closures. Poucos entendem o que acontece na memória.
Uma closure é uma função que captura o ambiente léxico onde foi criada. Não é cópia. É referência viva.
function criarContador() {
let count = 0; // essa variável "sobrevive" após criarContador() retornar
return {
incrementar: () => ++count,
valor: () => count,
};
}
const contador = criarContador();
console.log(contador.incrementar()); // 1
console.log(contador.incrementar()); // 2
console.log(contador.valor()); // 2
// A variável `count` não é acessível de fora. Encapsulamento real.
O bug clássico do loop
Esse bug aparece em entrevistas e em código real com frequência assustadora.
// ❌ BUGADO — todas as funções referenciam o mesmo `i`
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// Output: 3, 3, 3
// ✅ CORRETO — `let` cria escopo de bloco por iteração
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// Output: 0, 1, 2
// ✅ CORRETO (alternativa com closure explícita)
for (var i = 0; i < 3; i++) {
((j) => {
setTimeout(() => console.log(j), 100);
})(i);
}
// Output: 0, 1, 2
Com var, existe uma única variável i no escopo da função. As três closures apontam para ela. Quando o setTimeout executa, o loop já terminou e i vale 3.
Com let, cada iteração cria um novo binding. Cada closure captura sua própria cópia.
Closure e vazamento de memória
// ⚠️ Cuidado: closures mantêm referências vivas
function processarDados() {
const dadosGigantes = new Array(1_000_000).fill("🔥");
return function resumo() {
return dadosGigantes.length; // mantém `dadosGigantes` na memória
};
}
const fn = processarDados();
// `dadosGigantes` NUNCA será coletado pelo GC enquanto `fn` existir
---
Leia o artigo completo em [https://vivodecodigo.com.br/carreira/lacunas-javascript-webdev-tools-beginners](https://vivodecodigo.com.br/carreira/lacunas-javascript-webdev-tools-beginners)