var, let e const: diferenças e quando usar cada um
1. Introdução à declaração de variáveis em JavaScript
Desde o início do JavaScript, var era a única forma de declarar variáveis. Com a chegada do ECMAScript 2015 (ES6), let e const foram introduzidos para resolver problemas históricos de escopo e previsibilidade. A principal motivação foi oferecer controle mais granular sobre como e onde as variáveis existem no código, eliminando comportamentos confusos como hoisting sem inicialização e escopo de função que vazava para fora de blocos.
As três formas diferem em três aspectos fundamentais: escopo, hoisting e reatribuição. Entender essas diferenças é essencial para escrever código previsível em Node.js e React.
2. Escopo de variáveis: global, função e bloco
var possui escopo de função (function scope). Isso significa que uma variável declarada com var dentro de uma função é acessível em toda a função, independentemente de onde foi declarada. Já let e const possuem escopo de bloco (block scope), ficando restritas ao bloco {} onde foram declaradas.
function exemploEscopo() {
if (true) {
var x = 10; // escopo de função
let y = 20; // escopo de bloco
const z = 30; // escopo de bloco
}
console.log(x); // 10 (acessível)
console.log(y); // ReferenceError: y is not defined
console.log(z); // ReferenceError: z is not defined
}
Em loops, essa diferença é crítica:
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 3, 3, 3
}
for (let j = 0; j < 3; j++) {
setTimeout(() => console.log(j), 100); // 0, 1, 2
}
3. Hoisting e a zona temporal morta (Temporal Dead Zone)
Hoisting é o comportamento do JavaScript de mover as declarações para o topo do escopo antes da execução. var é içada e inicializada como undefined:
console.log(a); // undefined
var a = 5;
let e const também são içadas, mas não são inicializadas. Elas entram em uma zona temporal morta (TDZ) desde o início do escopo até a declaração:
console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 10;
console.log(c); // ReferenceError: Cannot access 'c' before initialization
const c = 15;
A TDZ previne acessos acidentais a variáveis antes de sua inicialização, tornando o código mais seguro.
4. Reatribuição e redeclaração
var permite tanto reatribuição quanto redeclaração no mesmo escopo:
var nome = "João";
var nome = "Maria"; // redeclaração permitida
nome = "Pedro"; // reatribuição permitida
let permite reatribuição, mas proíbe redeclaração:
let idade = 25;
idade = 26; // reatribuição permitida
let idade = 30; // SyntaxError: Identifier 'idade' has already been declared
const proíbe ambas:
const PI = 3.14159;
PI = 3.14; // TypeError: Assignment to constant variable
const PI = 3; // SyntaxError: Identifier 'PI' has already been declared
5. const e mutabilidade de objetos e arrays
É crucial entender que const impede a reatribuição da referência, mas não torna o valor imutável. Objetos e arrays declarados com const podem ter seu conteúdo modificado:
const pessoa = { nome: "Ana", idade: 30 };
pessoa.idade = 31; // permitido
pessoa.cidade = "São Paulo"; // permitido
// pessoa = {}; // TypeError: Assignment to constant variable
const numeros = [1, 2, 3];
numeros.push(4); // permitido
numeros[0] = 10; // permitido
// numeros = []; // TypeError: Assignment to constant variable
Para imutabilidade profunda, use Object.freeze():
const config = Object.freeze({
api: "https://api.exemplo.com",
timeout: 5000
});
config.timeout = 3000; // não gera erro em modo não estrito, mas não altera
6. Quando usar cada um: guia prático para Node.js e React
const como padrão — Use const para tudo que não precisa ser reatribuído. Em React, componentes, funções, hooks e imports devem usar const:
// React component
const MeuComponente = () => {
const [contador, setContador] = useState(0);
const handleClick = () => setContador(contador + 1);
return <button onClick={handleClick}>{contador}</button>;
};
let para variáveis que precisam ser reatribuídas — Contadores, acumuladores, flags:
// Node.js example
let total = 0;
for (let i = 0; i < dados.length; i++) {
total += dados[i].valor;
}
var apenas em código legado — Em projetos modernos, evite var. Use-o somente quando precisar do escopo de função explicitamente ou ao manter código antigo.
Regras de ouro:
- Projetos Node.js e React modernos: const como padrão, let quando necessário, var nunca (a menos que estrito legado)
- Prefira const em imports/exports de módulos
- Configure ESLint com no-var e prefer-const
7. Armadilhas comuns e boas práticas
Erro clássico com var em closures:
// Problema
var botoes = document.querySelectorAll('button');
for (var i = 0; i < botoes.length; i++) {
botoes[i].onclick = function() {
console.log(i); // sempre exibe o último valor
};
}
// Solução com let
for (let i = 0; i < botoes.length; i++) {
botoes[i].onclick = function() {
console.log(i); // valor correto para cada botão
};
}
Strict mode afeta var? Não diretamente, mas redeclarações acidentais em strict mode geram erros. let e const já são strict por padrão.
ESLint recomendado:
// .eslintrc.json
{
"rules": {
"no-var": "error",
"prefer-const": "error"
}
}
8. Exemplos comparativos em Node.js e React
Exemplo Node.js: servidor HTTP
// Antes (código legado)
var http = require('http');
var porta = 3000;
var servidor = http.createServer(function(req, res) {
res.end('Olá');
});
// Depois (moderno)
const http = require('http');
const PORTA = 3000;
const servidor = http.createServer((req, res) => {
res.end('Olá');
});
servidor.listen(PORTA);
Exemplo React: gerenciamento de estado
// Componente funcional com hooks
const Contador = () => {
const [count, setCount] = useState(0);
const [historico, setHistorico] = useState([]);
const incrementar = () => {
setCount(prev => {
const novoValor = prev + 1;
setHistorico([...historico, novoValor]);
return novoValor;
});
};
return (
<div>
<p>Contagem: {count}</p>
<button onClick={incrementar}>+</button>
</div>
);
};
Testando escopo no Node.js vs navegador — O comportamento é idêntico em ambos, mas o Node.js tem módulos (CommonJS/ESM) que afetam o escopo global. Em módulos ES6, var no topo não cria propriedade global, enquanto no navegador cria window.varName.
Referências
- MDN Web Docs: var — Documentação oficial sobre a declaração var, incluindo escopo de função e hoisting.
- MDN Web Docs: let — Documentação completa sobre let, escopo de bloco e zona temporal morta.
- MDN Web Docs: const — Guia oficial sobre const, imutabilidade de referência e mutabilidade de objetos.
- JavaScript.info: Variables — Tutorial interativo cobrindo var, let e const com exemplos práticos e exercícios.
- W3Schools: JavaScript Let — Explicação didática das diferenças entre var, let e const com exemplos simples.
- ESLint: no-var rule — Regra do ESLint que desencoraja o uso de var, com exemplos de código correto e incorreto.
- freeCodeCamp: var vs let vs const — Artigo detalhado comparando as três formas de declaração com exemplos em Node.js e React.