Executando verificação de segurança...
-2

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)
Carregando publicação patrocinada...
1

JavaScript é fracamente tipado. Isso significa que o motor faz conversões implícitas o tempo todo.

Isso aqui está errado, ser fracamente tipado não é realizar conversões implícitas o tempo todo.

É fazer isso do jeito errado

'2'+2 ser 4 não é necessariamente tipagem fraca, algumas linguagens como Lua podem se dar ao luxo, sem ser tipagem fraca, basta que a linguagem tenha operadores que ajustem a coerção implícita de tipos, a + b é sempre soma em Lua, então é OK fazer coerção implícita, em javascript pode ser adição ou concatenação então é um mecanismo fraco fazer a coerção implícita

1

Você tá certo e essa é uma correção que eu precisava ouvir. Tipagem fraca não é "fazer coerção implícita o tempo todo" são eixos ortogonais, e o seu exemplo com Lua é o contraexemplo perfeito que eu devia ter incluído desde o começo.

Acabei de reescrever a seção inteira do post com essa distinção explícita: tipagem forte/fraca é sobre o que acontece quando tipos incompatíveis se encontram numa operação; coerção implícita é uma escolha ortogonal de design. Lua é fortemente tipada e faz coerção porque o operador + é inequivocamente soma numérica, a conversão é determinística e não muda semântica. JavaScript é fraca porque o mesmo operador + tem dois significados possíveis (soma ou concatenação) dependendo do tipo dinâmico dos operandos em runtime. O engine precisa adivinhar a intenção, e a decisão muda silenciosamente o resultado. Esse é o ponto real.

Para deixar explícito no post, incluí uma tabela comparando Python (forte, sem coerção), Lua (forte, com coerção) e JavaScript (fraca, com coerção), exatamente como você formulou no comment.

Aproveitei para corrigir outras imprecisões no mesmo post enquanto estava nele: "Abstract Equality Comparison" (nome antigo da spec) virou "IsLooselyEqual" / "Loose Equality" (nome correto pós-ES2015); a seção do event loop foi reescrita separando linguagem (ECMAScript define Job Queue pra microtasks, só isso) de runtime (Node/libuv, browser/HTML spec, Bun/Zig, Deno/Tokio, cada um com sua implementação); e "React Concurrent Mode" (nome descontinuado) virou "Concurrent Rendering" com menção ao scheduler interno.

Obrigado de verdade pelas duas correções, a desse post e a do "Mapa Completo". Feedback técnico direto assim é mais útil que qualquer métrica de engajamento. Se rolar de você passar de novo pelos posts daqui do blog e ver mais algum deslize, manda bala que eu corrijo.