Nesta aula, vamos explorar as três maneiras de declarar variáveis em JavaScript: var, let e const. Cada uma tem características distintas em relação a escopo, hoisting e possibilidade de reatribuição. Compreender essas diferenças é fundamental para escrever código limpo e evitar bugs.

Declaração e atribuição

Declarar uma variável significa criar um espaço na memória para armazenar um valor. Atribuir é dar um valor a essa variável. Com var e let, você pode declarar sem atribuir inicialmente; com const, a atribuição é obrigatória na declaração.

// Declaração sem atribuição
var nome;
let idade;

// Declaração com atribuição
var cidade = 'São Paulo';
let ano = 2024;
const PI = 3.1415;

// Reatribuição
nome = 'Ana'; // permitido
idade = 25; // permitido
// PI = 3; // erro! const não pode ser reatribuída

Escopo de função vs bloco

var tem escopo de função: é visível em toda a função onde é declarada, mesmo dentro de blocos como if ou for. Já let e const têm escopo de bloco: só existem dentro do bloco onde foram declaradas (delimitado por chaves).

function exemplo() {
  if (true) {
    var x = 10; // escopo da função
    let y = 20; // escopo do bloco
    const z = 30; // escopo do bloco
  }
  console.log(x); // 10 - acessível
  console.log(y); // ReferenceError: y is not defined
  console.log(z); // ReferenceError: z is not defined
}

Hoisting

Hoisting é o comportamento de elevar declarações para o topo do escopo. Variáveis com var são içadas (hoisted) para o topo da função, mas sua atribuição permanece no lugar. let e const também sofrem hoisting, mas não são inicializadas, gerando erro se acessadas antes da declaração (Temporal Dead Zone).

console.log(a); // undefined (hoisting de var)
var a = 5;

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

console.log(c); // ReferenceError: Cannot access 'c' before initialization
const c = 15;

Quando usar cada uma

Use const sempre que o valor não precisar ser reatribuído (é a recomendação padrão). Use let quando precisar reatribuir o valor (como contadores de loops). Evite var em código moderno, pois seu escopo de função e comportamento de hoisting podem causar confusão; prefira let e const para escopo de bloco.

// Boas práticas:
const nome = 'Maria'; // valor constante
let contador = 0; // será reatribuído
for (let i = 0; i < 10; i++) { // i tem escopo de bloco
  contador += i;
}
// var não é recomendado

Referências

Exercícios

  1. Qual a diferença entre escopo de função e escopo de bloco? Dê um exemplo.
  2. Escopo de função significa que a variável é visível em toda a função, independente de blocos internos. Escopo de bloco significa que a variável só existe dentro do bloco (entre chaves) onde foi declarada. Exemplo:
    function teste() {
      if (true) {
        var a = 1; // escopo de função
        let b = 2; // escopo de bloco
      }
      console.log(a); // 1
      console.log(b); // ReferenceError
    }
  3. O que é hoisting? Como ele se comporta com var, let e const?
  4. Hoisting é o comportamento de mover declarações para o topo do escopo. Com var, a declaração é içada e a variável fica undefined até a atribuição. Com let e const, a declaração é içada mas a variável não é inicializada, gerando erro se acessada antes da linha de declaração (Temporal Dead Zone).
  5. Por que não é recomendado usar var em código moderno?
  6. Porque var tem escopo de função, o que pode causar vazamento de variáveis para fora de blocos, e seu hoisting pode gerar comportamentos inesperados. let e const têm escopo de bloco e são mais previsíveis, seguindo as melhores práticas atuais.
  7. Escreva um código que demonstre a diferença entre let e const.
  8. let x = 10;
    x = 20; // permitido
    
    const y = 30;
    y = 40; // TypeError: Assignment to constant variable
  9. Qual a saída do código abaixo? Explique.
    console.log(a);
    var a = 5;
    console.log(b);
    let b = 10;
  10. A saída é: undefined e depois um erro ReferenceError: Cannot access 'b' before initialization. Com var, o hoisting faz a declaração ser içada e a variável fica undefined até a atribuição. Com let, a variável está na Temporal Dead Zone até a declaração, então acessá-la antes gera erro.