5 de setembro de 2026/3 min de leitura

var, let, const e Escopo: O Que Realmente Muda

Se você já olhou JavaScript escrito antes de mais ou menos 2015 e JavaScript escrito hoje, uma diferença salta aos olhos na hora: var estava em todo lugar, e hoje você vê principalmente let e const. Entender por quê é uma das formas mais rápidas de parar de se confundir com bugs que parecem não fazer sentido.

O que "escopo" significa

Escopo é só a resposta para uma pergunta: "De onde no meu código eu consigo acessar essa variável?"

function cumprimentar() {
  const mensagem = "Olá!"
  console.log(mensagem) // funciona — estamos dentro da mesma função
}

console.log(mensagem) // ❌ ReferenceError: mensagem não está definida

mensagem só existe dentro de cumprimentar. Fora dela, é como se essa variável nunca tivesse existido. Isso é escopo: uma variável só é visível dentro do "bloco" (basicamente, a região entre { }) onde foi criada — e em qualquer bloco aninhado dentro desse.

A diferença central: escopo de função vs escopo de bloco

Essa é a diferença real entre var e let/const — todo o resto decorre disso.

  • var tem escopo de função. Ele ignora completamente blocos { } como if e for — só é contido pela função mais próxima (ou pelo escopo global, se não houver função).
  • let e const têm escopo de bloco. Eles respeitam o { } — uma variável declarada dentro de um bloco if não vaza para fora dele.

Aqui está o bug que isso causa na prática:

if (true) {
  var x = 10
}
console.log(x) // 10 — o var vazou para fora do bloco if!

if (true) {
  let y = 10
}
console.log(y) // ❌ ReferenceError — o let ficou dentro do bloco, como esperado

Esse "vazamento" é exatamente por que o var causa bugs confusos em projetos maiores: uma variável que você pensava ser temporária e local, na verdade, ainda está viva em algum lugar onde não deveria estar.

O bug clássico do loop

Este é o exemplo mais usado para explicar por que o var causa problemas reais:

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0)
}
// Imprime: 3, 3, 3  (e não 0, 1, 2, como você esperaria!)

Isso acontece porque o var não cria um novo i a cada repetição do loop — existe apenas um i, compartilhado por todo o loop, e quando os callbacks do setTimeout finalmente rodam, o loop já terminou e i vale 3.

for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0)
}
// Imprime: 0, 1, 2 — como esperado

O let cria um i novo a cada repetição, então cada callback captura seu próprio valor separado. Esse único comportamento é boa parte do motivo pelo qual o let substituiu o var como padrão.

let vs const

Uma vez com escopo de bloco resolvido, a escolha entre let e const é simples: é sobre se você pretende reatribuir a variável, não sobre se o conteúdo dela pode mudar.

const nome = "Ana"
nome = "Beatriz" // ❌ TypeError — const não pode ser reatribuída

const usuario = { nome: "Ana" }
usuario.nome = "Beatriz" // ✅ totalmente válido — não estamos reatribuindo `usuario`,
                          //    estamos alterando o objeto para o qual ele aponta

const não significa "congelado para sempre" — significa "essa variável sempre vai apontar para o mesmo valor". Se esse valor for um objeto ou array, o conteúdo dele ainda pode mudar; você só não pode fazer usuario apontar para um objeto completamente diferente.

Uma regra prática

  • Use const por padrão, para tudo.
  • Mude para let só quando souber que a variável precisa ser reatribuída (um contador de loop, um valor que muda com base numa condição).
  • Evite var — não existe situação no JavaScript moderno onde ele faz algo que let ou const não façam melhor e de forma mais previsível.

Isso não é preferência de estilo — seguir essa regra elimina uma categoria inteira de bugs (o bug do loop acima, e suas variações) antes mesmo de você precisar depurá-los.