Voltar ao blog
javascriptfrontendfundamentos

Hoisting e Scope: como o JavaScript decide onde as coisas existem

11 de abril de 2026 às 10:18 - Por Larissa Santos

Hoisting e Scope: como o JavaScript decide onde as coisas existem

Se você já tomou um undefined inesperado, ou um ReferenceError em um lugar que parecia impossível, hoisting ou scope estavam envolvidos. Esses dois conceitos definem como o JavaScript enxerga o seu código antes e durante a execução.

Por que isso importa

O JavaScript não lê o código de cima para baixo da forma que parece. Antes de executar qualquer linha, o engine passa por uma fase de compilação onde registra todas as declarações. É nessa fase que o hoisting acontece.

Entender isso muda como você interpreta erros, como você organiza funções e por que var se comporta de forma diferente de let e const em contextos que parecem idênticos.

O que é hoisting

Hoisting é o comportamento do engine de registrar declarações antes da execução começar. Popularmente se diz que as declarações "sobem para o topo do código", mas o que acontece de verdade é que elas são processadas na fase de compilação, antes de qualquer atribuição de valor.

O que sobe e o que fica no lugar depende do tipo da declaração.

Com var, a declaração sobe, mas o valor não:

js
console.log(nome) // undefined
var nome = 'Ana'
console.log(nome) // "Ana"

O que o engine processa é algo como:

js
var nome // içado, valor = undefined
console.log(nome) // undefined
nome = 'Ana'
console.log(nome) // "Ana"

A linha var nome vai ao topo. A atribuição = "Ana" permanece exatamente onde foi escrita.

Com let e const, o comportamento muda. As declarações também são içadas, mas ficam em uma zona morta temporal, a Temporal Dead Zone, até que a linha de declaração seja atingida em execução. Qualquer acesso antes disso gera um ReferenceError:

js
console.log(idade) // ReferenceError
let idade = 25

Isso não é um bug. É intencional. O comportamento foi projetado exatamente para evitar leitura de variáveis em estado indefinido, um problema silencioso que var carrega consigo.

Com function declarations, o hoisting é completo. Declaração e corpo sobem juntos:

js
saudar() // "Olá!"

function saudar() {
  console.log('Olá!')
}

Com function expressions e arrow functions, a função é apenas um valor atribuído a uma variável. O hoisting se aplica à variável, não à função:

js
cumprimentar() // TypeError: cumprimentar is not a function

var cumprimentar = function () {
  console.log('Oi!')
}

var cumprimentar é içado como undefined. Chamar undefined como função gera um TypeError. Se fosse const ou let, seria um ReferenceError pela TDZ.

O que é scope

Scope define onde uma variável pode ser acessada. O JavaScript trabalha com três níveis.

Scope global é o nível mais externo. Variáveis declaradas aqui estão disponíveis em qualquer parte do código. Em aplicações maiores, isso vira um problema rápido: qualquer coisa pode ler e sobrescrever essas variáveis.

Function scope isola variáveis dentro de funções. Uma variável declarada com var dentro de uma função não existe fora dela:

js
function calcular() {
  var resultado = 42
}

console.log(resultado) // ReferenceError

Block scope é o comportamento de let e const. Qualquer bloco delimitado por {} cria um escopo próprio:

js
if (true) {
  let mensagem = 'dentro do bloco'
  console.log(mensagem) // "dentro do bloco"
}

console.log(mensagem) // ReferenceError

var não respeita block scope. Ele respeita apenas o escopo da função mais próxima:

js
if (true) {
  var valor = 10
}

console.log(valor) // 10

Esse é um dos motivos pelos quais var costuma ser fonte de bugs difíceis de rastrear. Você declara dentro de um if, de um for, de qualquer bloco, e a variável vaza para o escopo da função inteira.

Como os dois se conectam

Hoisting e scope não operam de forma isolada. O hoisting sempre acontece dentro de um escopo. Uma variável é içada para o topo do escopo em que foi declarada, não para o topo do arquivo.

js
function exemplo() {
  console.log(x) // undefined, não ReferenceError
  var x = 5
}

console.log(x) // ReferenceError

Dentro da função, x é içado para o topo do function scope. Fora da função, ela simplesmente não existe.

Isso explica um comportamento que confunde bastante em loops com var:

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

var i não fica presa no bloco do for. Ela é içada para o escopo da função (ou global) e existe como uma única variável. Quando os callbacks executam, o loop já terminou e i é 3.

Com let, cada iteração cria um escopo próprio:

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

Conclusão

Hoisting e scope são a base de como o JavaScript organiza o seu código antes de executá-lo. Quando você entende que o engine processa declarações antes de valores, e que cada tipo de declaração tem um comportamento diferente nesse processo, os erros que antes pareciam aleatórios começam a fazer sentido.

var em loop vaza para fora do bloco porque var não respeita block scope. undefined antes da declaração acontece porque a declaração subiu mas o valor não. ReferenceError em let acontece porque a TDZ protege você de ler uma variável que ainda não foi inicializada.

O JavaScript não está sendo imprevisível. Ele está seguindo regras bem definidas de onde as coisas vivem e quando ficam disponíveis.

Referências

Feito com e Vue.js
2026 © Larissa Santos