Dia 2 — Variáveis, let e const

Informatica · Conteudo · publicado em 02/10/2026
Dia 2 de 13

Variáveis, let e const

Aula 1

let, const e o fim de var

let, const e o fim de var

Variável é um nome que aponta para um valor. Declarar é criar esse nome; atribuir é colocar um valor dentro dele. São duas frases separadas pelo sinal de igual.

let idade = 30;      // declara e já atribui
let cidade;          // declara sem valor: vale undefined
cidade = 'Recife';   // atribui depois

let é a forma usada hoje. var é a forma antiga, de 1995, e só aparece em código velho: ela respeita menos regras, e as regras soltas viram bug.

Nenhum dos três entrega um valor imutável no sentido forte da palavra. const é o que mais se aproxima, e mesmo assim trava só a ligação entre o nome e o valor: o conteúdo apontado continua podendo mudar, assunto da aula 2.

Identificador: o nome da variável

O nome pode ter letra, número, _ e $, e não pode começar por número. camelCase é a convenção do curso: primeira letra minúscula, sem espaço, palavras seguintes com maiúscula.

EscreveValePor quê
let idade = 30;simnome simples
let totalVendas = 0;simcamelCase, padrão do curso
let _interno = 1;simcomeça com _
let $elemento = 1;sim$ é permitido
let 2total = 1;nãocomeça com número
let idade = 30nãofalta o ponto e vírgula

Palavra reservada não pode ser nome de variável: let, const, var, if, for, function, return, class, new, typeof. idade e Idade são nomes diferentes, porque a JavaScript diferencia maiúscula de minúscula.

Tipagem dinâmica

A variável não tem tipo. Quem tem tipo é o valor que está dentro dela, e esse tipo pode mudar a qualquer atribuição.

let idade = 30;          // typeof idade devolve 'number'
idade = 'trinta e um';   // typeof idade devolve 'string'
idade = 31;              // volta a ser 'number'

Não existe int, float ou string na declaração. Isso torna o código curto e tira do programador uma decisão que o erro só apareceria depois, longe do lugar do defeito.

Escopo de bloco: o que fica entre chaves

O escopo de bloco é o que fica entre { }. Um let declarado dentro de um bloco só existe dentro dele. Passou do bloco, o nome desapareceu.

const painel = 'título principal';
{
  const painel = 'título da seção';   // outro nome, mesmo texto
  console.log(painel);                // 'título da seção'
}
console.log(painel);                  // 'título principal'

Os dois painel não se atrapalham porque estão em blocos diferentes. O let e o const sobem para dentro do bloco; nunca saem dele.

var é o contrário: declarada dentro de chaves, ela continua existindo depois do bloco. E ela sobe para o topo da função inteira antes de qualquer linha rodar, o chamado hoisting.

Usar um let antes da linha em que ele é declarado dá ReferenceError. Com var o valor antigo era undefined em silêncio, e o erro aparecia três linhas depois, no lugar errado. Esse é o motivo da troca.

A redeclaração é um dos motivos do fim do var: declarar de novo, no mesmo bloco, um nome que já existe. Com let e com const isso nem chega a rodar — o navegador acusa o arquivo inteiro antes de executar a primeira linha. Com var era permitido, e quem redeclarava por engano só descobria depois.

var, let e const lado a lado

varletconst
Respeita o bloconãosimsim
Redeclarar no mesmo blocopermitidoerroerro
Usar antes de declararundefinederroerro
Reatribuirsimsimerro
Aparece em código novonãosimsim

var falhou nos quatro primeiros itens da tabela, e é por isso que saiu de moda. Código novo usa const e let, sem exceção.

Exemplo

'use strict';
// ============================================================
// AULA 1 - let, const e o fim de var
//
// `let` e `const` declaram a variável; `var` é a forma antiga, que
// só aparece em código velho. Este exemplo declara com as três e
// mostra o que cada uma faz de diferente — inclusive os erros,
// capturados para o script não morrer no meio.
//
// Os `console.error` deste arquivo saem no fim da saída do terminal,
// separados do texto normal. A linha de `console.log` que vem logo
// depois de cada um é a que aparece na página.
// ============================================================

// Declarar é uma frase; atribuir é outra. `let` cria a ligação
// entre o nome e o lugar onde o valor mora, `=` coloca o valor.
let idade = 30;
let cidade;
cidade = 'Recife';

console.log('--- declarar e atribuir ---');
console.log('idade:', idade);
console.log('cidade:', cidade, '(declarada e atribuída em linhas separadas)');

// A tipagem é dinâmica: a variável não tem tipo fixo, o VALOR tem.
// Trocar o tipo é permitido e não deixa rastro.
idade = 'trinta e um';
console.log('');
console.log('--- tipagem dinâmica ---');
console.log('idade agora é texto:', idade, typeof idade);
idade = 31;
console.log('idade voltou a ser número:', idade, typeof idade);

// O escopo de bloco é o que fica entre chaves. Um `let` declarado
// dentro do bloco só existe dentro dele, e o mesmo nome pode se
// repetir depois do bloco.
const painel = 'título principal';
{
  const painel = 'título da seção';
  console.log('');
  console.log('--- escopo de bloco ---');
  console.log('dentro do bloco:', painel);
}
console.log('fora do bloco:', painel, '(o nome de dentro não vazou para fora)');

// Redeclarar o mesmo `let` no mesmo bloco é erro de sintaxe: o
// navegador acusa o arquivo inteiro antes de executar a primeira
// linha. O `eval` existe só para este exemplo poder mostrar a
// mensagem do erro e seguir em frente.
console.log('');
console.log('--- o que dá erro, e de propósito ---');
console.log('1) redeclarar o mesmo let no mesmo bloco');
try {
  eval('let idade = 40; let idade = 50;');
} catch (erro) {
  console.error(erro.constructor.name + ': ' + erro.message);
  console.log('  erro:', erro.constructor.name, '-', erro.message);
}

// Usar a variável antes da linha em que ela é declarada também é
// erro. Com `var` o valor antigo era `undefined` em silêncio; com
// `let` o acesso é barrado e o erro diz o que aconteceu.
console.log('2) usar a let antes da linha que a declara');
try {
  console.log(media);
  let media = 7.5;
} catch (erro) {
  console.error(erro.constructor.name + ': ' + erro.message);
  console.log('  erro:', erro.constructor.name, '-', erro.message);
}

// `var` não respeita o bloco: declarada dentro de chaves, ela
// continua existindo depois. E ela sobe para o topo da função
// inteira, o chamado hoisting.
{
  var contador = 1;
  console.log('');
  console.log('--- var ignora o bloco ---');
  console.log('declarado dentro do bloco:', contador);
}
console.log('declarado com var, lido fora:', contador);

// O `typeof` é a única consulta que não quebra em variável que não
// existe: é assim que o código descobre o que já foi criado. Ele
// também não cria a variável, então ler o nome depois continua erro.
console.log('');
console.log('--- typeof não quebra em nome que não existe ---');
console.log('typeof naoDeclarada:', typeof naoDeclarada);
console.log('typeof contador:', typeof contador, '(o que existe, devolve o tipo)');
console.log('3) ler o nome que não foi declarado');
try {
  console.log(naoDeclarada);
} catch (erro) {
  console.error(erro.constructor.name + ': ' + erro.message);
  console.log('  erro:', erro.constructor.name, '-', erro.message);
}

Saída real

--- declarar e atribuir ---
idade: 30
cidade: Recife (declarada e atribuída em linhas separadas)

--- tipagem dinâmica ---
idade agora é texto: trinta e um string
idade voltou a ser número: 31 number

--- escopo de bloco ---
dentro do bloco: título da seção
fora do bloco: título principal (o nome de dentro não vazou para fora)

--- o que dá erro, e de propósito ---
1) redeclarar o mesmo let no mesmo bloco
  erro: SyntaxError - Identifier 'idade' has already been declared
2) usar a let antes da linha que a declara
  erro: ReferenceError - Cannot access 'media' before initialization

--- var ignora o bloco ---
declarado dentro do bloco: 1
declarado com var, lido fora: 1

--- typeof não quebra em nome que não existe ---
typeof naoDeclarada: undefined
typeof contador: number (o que existe, devolve o tipo)
3) ler o nome que não foi declarado
  erro: ReferenceError - naoDeclarada is not defined
Aula 2

const, escopo de bloco e o que não pode mudar

const, escopo de bloco e o que não pode mudar

const declara uma variável cujo nome não pode mais ser ligado a outro valor. O valor apontado continua podendo mudar — essa é a distinção que separa esta aula da anterior.

const configuracao = { tema: 'escuro' };
configuracao.tema = 'claro';    // permitido: mudou o conteúdo
configuracao = { tema: 'escuro' };  // TypeError: mudou a ligação

O const trava o nome, não o conteúdo. A analogia que resolve: o nome é a etiqueta colada na caixa, e o que está dentro da caixa continua à disposição.

const com objeto e com array

O erro mais comum do curso nasce aqui. const objeto = {} não diz que o objeto é imutável: diz que a variável objeto não vai apontar para outro objeto.

const lista = ['Ana', 'Bia'];
lista.push('Cau');        // o array cresce: permitido
lista.length;             // 3
lista = ['Ana'];          // TypeError: agora sim, proibido

Com objeto, a propriedade pode ser criada do nada, alterada e apagada. Com array, além disso, dá para acrescentar, remover e reordenar. Tudo isso continua funcionando sob um const, porque o que o const proíbe é a reatribuição do nome, não a mudança do conteúdo mutável apontado.

Object.freeze: o degrau seguinte

Object.freeze congela o conteúdo, não só a ligação. Depois do congelamento, escrever na propriedade é TypeError.

FunçãoAssinaturaExemplo
Object.freezeObject.freeze<T>(o: T): Readonly<T>Object.freeze({ versao: 1 })
Object.isFrozenObject.isFrozen(o: object): booleanObject.isFrozen(config)

O congelamento é raso: congela as propriedades do primeiro nível, e um objeto dentro do objeto continua mutável. Ele também não sobrevive a JSON.parse, que cria um objeto novo.

const por omissão

A regra prática do curso é uma só: const por omissão, let só quando o valor realmente muda. Isso dá um padrão de leitura — quem lê o let já sabe onde procurar a mudança.

A comparação const versus let tem uma diferença e uma semelhança. A semelhança: os dois respeitam o bloco e os dois acusam uso antes da declaração, igual ao const da aula anterior. A diferença: o let pode receber outro valor na linha de baixo, e o const não.

A pergunta que decide é curta, e é a mesma que o aluno faz quando digita quando usar const: esse nome recebe outro valor em algum momento do código? Se não recebe, é const; se recebe, é let. A segunda parte da regra é preferir const — escrever let exige um motivo, escrever const não.

SituaçãoDeclare com
configuração, etiqueta, tabela de verdadeconst
funçãoconst
item do for...ofconst
contador, accumulating resultadolet
texto que a pessoa digita no campolet

const com função não é um caso especial: função é valor como outro, e a seta vai atrás de um nome declarado com const.

const somar = (a, b) => a + b;
somar(2, 3);   // 5

A seta => é a arrow function, assunto da aula 7. Aqui importa só o const na frente dela.

const em laço

No for...of, const é a escolha certa e não é preciosismo: cada volta do laço cria um nome novo, então o código do corpo não tem como estragar o valor da volta seguinte.

for (const nome of ['Ana', 'Bia', 'Cau']) {
  console.log(nome);
}

Com let, o mesmo nome é reatribuído a cada volta. Com var, é a mesma variável desde o começo da função, e um setTimeout disparado no laço acabou mostrando sempre o último valor.

const não significa "fixo" no sentido de "igual para sempre". Significa "esta referência não muda". Escrever const total = 0; total++; dá TypeError — e essa é a intenção: o código estava dizendo que o total nunca mudava, e o ++ está dizendo o contrário.

Exemplo

'use strict';
// ============================================================
// AULA 2 - const, escopo de bloco e o que não pode mudar
//
// `const` proíbe a reatribuição: o nome aponta para o mesmo valor
// para sempre. O que o valor aponta continua podendo mudar, e é
// aí que nasce a confusão mais comum do curso.
//
// Os `console.error` deste arquivo saem no fim da saída do terminal,
// separados do texto normal. A linha de `console.log` que vem logo
// depois de cada um é a que aparece na página.
// ============================================================

// `const` impede trocar a ligação do nome, não impede mexer no
// conteúdo apontado.
const configuracao = { tema: 'escuro', idioma: 'pt-BR' };
console.log('--- const com objeto ---');
console.log('antes:', configuracao);
configuracao.tema = 'claro';
configuracao.aba = 'relatório';
console.log('depois de mexer nas propriedades:', configuracao);

const lista = ['Ana', 'Bia'];
console.log('');
console.log('--- const com array ---');
lista.push('Cau');
console.log('depois de push:', lista, '(o array cresce, a ligação não muda)');

// Reatribuir a própria variável é o que o `const` proíbe. O erro
// é TypeError e sai na hora, sem computador errado em silêncio.
console.log('');
console.log('--- o que dá erro, e de propósito ---');
console.log('1) trocar a variável inteira');
try {
  configuracao = { tema: 'claro' };
} catch (erro) {
  console.error(erro.constructor.name + ': ' + erro.message);
  console.log('  erro:', erro.constructor.name, '-', erro.message);
}

console.log('2) trocar um array inteiro por outro');
try {
  lista = [];
} catch (erro) {
  console.error(erro.constructor.name + ': ' + erro.message);
  console.log('  erro:', erro.constructor.name, '-', erro.message);
}

// Object.freeze é o degrau seguinte: congela o CONTEÚDO, não só a
// ligação. Depois do freeze, escrever na propriedade é erro.
const congelado = Object.freeze({ versao: 1 });
console.log('');
console.log('--- Object.freeze ---');
console.log('antes da tentativa, versão:', congelado.versao);
console.log('3) mexer no conteúdo de um objeto congelado');
try {
  congelado.versao = 2;
} catch (erro) {
  console.error(erro.constructor.name + ': ' + erro.message);
  console.log('  erro:', erro.constructor.name, '-', erro.message);
}
console.log('depois da tentativa, versão:', congelado.versao, '(continua 1)');

// A regra prática do curso: `const` por omissão, `let` só quando o
// valor realmente muda. Uma função é um valor como outro, e pode
// ser `const`.
const somar = (a, b) => a + b;
console.log('');
console.log('--- const por omissão ---');
console.log('somar(2, 3) =', somar(2, 3));
console.log('const vale para número, texto, objeto, array e função');

// Em laço, `const` é o lugar onde ele brilha: cada volta cria um
// nome novo e o valor nunca é reatribuído, então não há como o
// código do corpo estragar o valor da volta seguinte.
console.log('');
console.log('--- const em laço ---');
for (const nome of ['Ana', 'Bia', 'Cau']) {
  console.log('  processando', nome);
}

// A diferença que resume a aula inteira: `const` trava o nome,
// `let` destrava. Os dois respeitam o bloco; `var` não respeita
// nenhum, e por isso saiu de moda.
console.log('');
console.log('--- onde cada um vale ---');
console.log('const: valor fixo (configuração, função, etiqueta, item do laço)');
console.log('let: valor que muda (contador, texto do campo, seleção)');
console.log('var: só em código antigo');
console.log('');
{
  const unico = 'só existe aqui dentro';
  console.log('dentro do bloco, único =', unico);
}
console.log('fora do bloco, o nome já não existe mais');

Saída real

--- const com objeto ---
antes: { tema: 'escuro', idioma: 'pt-BR' }
depois de mexer nas propriedades: { tema: 'claro', idioma: 'pt-BR', aba: 'relatório' }

--- const com array ---
depois de push: [ 'Ana', 'Bia', 'Cau' ] (o array cresce, a ligação não muda)

--- o que dá erro, e de propósito ---
1) trocar a variável inteira
  erro: TypeError - Assignment to constant variable.
2) trocar um array inteiro por outro
  erro: TypeError - Assignment to constant variable.

--- Object.freeze ---
antes da tentativa, versão: 1
3) mexer no conteúdo de um objeto congelado
  erro: TypeError - Cannot assign to read only property 'versao' of object '#<Object>'
depois da tentativa, versão: 1 (continua 1)

--- const por omissão ---
somar(2, 3) = 5
const vale para número, texto, objeto, array e função

--- const em laço ---
  processando Ana
  processando Bia
  processando Cau

--- onde cada um vale ---
const: valor fixo (configuração, função, etiqueta, item do laço)
let: valor que muda (contador, texto do campo, seleção)
var: só em código antigo

dentro do bloco, único = só existe aqui dentro
fora do bloco, o nome já não existe mais