Em JavaScript, variáveis são contêineres para armazenar dados. A linguagem oferece três palavras-chave para declarar variáveis: var, let e const. Cada uma possui regras específicas de escopo, hoisting e reatribuição. Compreender essas diferenças é essencial para escrever código limpo, previsível e livre de bugs.

Nesta aula, veremos na prática como cada declaração se comporta, desde a simples atribuição até os mecanismos internos de elevação (hoisting). Ao final, você terá clareza sobre quando usar cada uma.

Declaração e atribuição

A declaração de uma variável cria um identificador no escopo atual. A atribuição armazena um valor nesse identificador. Com var, let e const, a sintaxe é semelhante, mas o comportamento difere.

var permite redeclarar a mesma variável no mesmo escopo, enquanto let e const não. const exige que a variável seja inicializada no momento da declaração e não pode ser reatribuída (embora objetos e arrays possam ter seus conteúdos alterados).

// Declaração e atribuição com var
var nome = 'Maria';
var nome = 'João'; // permitido (redeclaração)
console.log(nome); // 'João'

// Com let
let idade = 25;
// let idade = 30; // Erro: Identificador 'idade' já declarado
idade = 30; // permitido (reatribuição)
console.log(idade); // 30

// Com const
const PI = 3.14159;
// PI = 3; // Erro: Atribuição a variável constante.
// const cor; // Erro: falta inicializador
console.log(PI); // 3.14159

Note que const não impede a mutação de objetos. O vínculo é constante, não o valor.

Escopo de função vs bloco

Escopo define onde a variável é acessível. var tem escopo de função (ou global, se declarada fora de função). Já let e const têm escopo de bloco (delimitado por {}).

Isso significa que var vaza para fora de blocos como if ou for, enquanto let e const ficam restritas ao bloco. Exemplo:

function exemploEscopo() {
  if (true) {
    var a = 1;   // escopo da função
    let b = 2;   // escopo do bloco if
    const c = 3; // escopo do bloco if
  }
  console.log(a); // 1 (acessível)
  // console.log(b); // Erro: b não está definida
  // console.log(c); // Erro: c não está definida
}
exemploEscopo();

Em loops for, a diferença é crucial. Com var, a variável do contador vaza para fora do loop; com let, ela fica confinada ao bloco do for.

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // imprime 3, 3, 3
}

for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log(j), 100); // imprime 0, 1, 2
}

Isso ocorre porque var usa o mesmo i para todas as iterações, enquanto let cria uma nova variável a cada iteração.

Hoisting

Hoisting é o comportamento de elevar declarações para o topo do escopo antes da execução. Em JavaScript, as declarações com var são içadas (hoisted) e inicializadas com undefined. Já let e const também são içadas, mas não são inicializadas, ficando em uma "zona morta temporal" (temporal dead zone) até a declaração.

Exemplo com var:

console.log(x); // undefined (não dá erro)
var x = 5;
console.log(x); // 5

Com let ou const:

// console.log(y); // Erro: Cannot access 'y' before initialization
let y = 10;
console.log(y); // 10

// console.log(z); // Erro: Cannot access 'z' before initialization
const z = 20;
console.log(z); // 20

Na prática, o JavaScript move a declaração (var y) para o topo, mas a inicialização permanece no lugar. Para let e const, a declaração é içada, mas a variável não pode ser acessada antes da linha onde é declarada.

Quando usar cada uma

Atualmente, var é considerada obsoleta para novos projetos. Prefira const sempre que o valor não precisar ser reatribuído (a maioria dos casos). Use let quando precisar reatribuir (contadores, acumuladores). Evite var a menos que esteja mantendo código legado ou precise de compatibilidade com ambientes muito antigos.

Boas práticas:

  • Use const por padrão.
  • Use let apenas quando for reatribuir.
  • Nunca use var em código novo.
  • Declare variáveis no topo do escopo (bloco) para evitar confusão com hoisting.

Exemplo prático:

// Preferido
const nome = 'Ana';
let contador = 0;
contador++;

// Evitado
var nome2 = 'João';

Referências

Exercícios

  1. Qual a diferença de escopo entre var e let? Dê um exemplo.
  2. ✓ Resposta: var tem escopo de função (ou global), enquanto let tem escopo de bloco. Exemplo: dentro de um if, var vaza para fora, let não.
  3. O que acontece se tentarmos acessar uma variável let antes de sua declaração? E com var?
  4. ✓ Resposta: Com let ou const, ocorre um erro de referência (ReferenceError) devido à zona morta temporal. Com var, retorna undefined (hoisting).
  5. Corrija o código abaixo para que imprima 0, 1, 2 usando let ou const no lugar de var:
  6. for (var i = 0; i < 3; i++) {
      setTimeout(() => console.log(i), 100);
    }

    ✓ Resposta: Basta trocar var i por let i:
    for (let i = 0; i < 3; i++) {
      setTimeout(() => console.log(i), 100);
    }
  7. É possível redeclarar uma variável const no mesmo escopo? E reatribuir?
  8. ✓ Resposta: Não é possível redeclarar nem reatribuir uma variável const. Ela deve ser inicializada na declaração e seu vínculo é imutável.
  9. Escreva um exemplo que demonstre a zona morta temporal (temporal dead zone) com let.
  10. ✓ Resposta:
    console.log(x); // ReferenceError: Cannot access 'x' before initialization
    let x = 10;