DEV Community

Cover image for Hoisting: Que bruxaria é essa?!
ananopaisdojavascript
ananopaisdojavascript

Posted on • Updated on

Hoisting: Que bruxaria é essa?!

Definição

Hoisting é um comportamento do motor do JS. É quando esse mesmo motor "catapulta" a variável ou função para o topo do escopo. Eu expliquei rapidamente o que é escopo no artigo Var, let e const: Qual é a diferença? e vou deixar o link aqui.

Tentando entender...

Vou usar o console.log() para chamar alguma coisa

console.log(a); // ReferenceError: a is not defined
Enter fullscreen mode Exit fullscreen mode

Como o motor do JS não sabe do que se trata (é variável? é função? é objeto?), retornou um erro dizendo que a não existe. Agora, se eu crio uma variável de mesmo nome a debaixo desse console.log(), o que será que acontece?!

console.log(a); // undefined
var a;
Enter fullscreen mode Exit fullscreen mode

Eu criei uma variável depois do console.log(), não a declarei (não coloquei valor algum nela) e retornou undefined. Por que isso aconteceu?! É porque o motor do JS encontrou essa variável e a jogou para a parte de cima do escopo. É como se eu tivesse criado a variável antes do console.log(), o que não aconteceu.

var a;
console.log(a); // undefined
Enter fullscreen mode Exit fullscreen mode

Mas... e se tivesse declarado um valor em a? O que poderia acontecer?

console.log(a); // undefined
var a = 20;
Enter fullscreen mode Exit fullscreen mode

O resultado seria o mesmo. O retorno seria undefined porque a apareceu antes de ser declarada. O motor do JS entenderia dessa forma:

var a;
console.log(a); // undefined
a = 20;
Enter fullscreen mode Exit fullscreen mode

Ele entenderia que a variável a foi criada sem qualquer valor e que a declaração só apareceu depois de ter sido chamada. Muito louco, não? E se eu colocar o console.log() depois de a já ter sido declarada, o que vai acontecer?

console.log(a); // undefined
var a = 20;
console.log(a); // 20
Enter fullscreen mode Exit fullscreen mode

Hum... nesse caso o valor aparece normalmente. Tudo por conta de var que não tem limitação. Talvez seja uma das razões pelas quais seu uso não é recomendado... E por falar em loucura, que tal testar esse raciocínio com let e const?

Testando com let

Vou fazer a mesma coisa, só que com let no lugar de var. O que será que acontece?!

console.log(a); // ReferenceError: Cannot access 'a' before initialization
let a;
Enter fullscreen mode Exit fullscreen mode

Epa!! A mensagem de erro agora é completamente diferente! Está dizendo que a não pode ser acessada antes da inicialização! Por que isso aconteceu?! A resposta nos leva ao...

Temporal Dead Zone

Parece nome de filme de terror, mas é que essa mensagem de erro é conhecida na comunidade com esse nome (vai entender...). É o seguinte: o motor do JS reconhece que a variável a existe, mas que não pode acessá-la antes da declaração, ou seja, existe uma "zona morta" entre a variável e sua declaração. Lembrando que, apesar de podermos fazer declarações de valores diferentes, let é um pouco mais limitado que var no que se refere ao escopo. Agora vou declarar um valor para a e chamar o console.log() em seguida. O que será que vai acontecer?!

console.log(a); // ReferenceError: Cannot access 'a' before initialization
let a = 123;
console.log(a); // ReferenceError: Cannot access 'a' before initialization
Enter fullscreen mode Exit fullscreen mode

Olha lá! A mensagem de erro permaneceu a mesma! É porque a variável a foi usada antes de ter sido declarada e o motor do JS pode ter entendido que tem muita variável com o mesmo nome e deveria "frear" essa "bagunça". Lembrando: let é mais limitado do que var. O mesmo comportamento se aplica ao const...

console.log(a); // ReferenceError: Cannot access 'a' before initialization
const a = 123;
Enter fullscreen mode Exit fullscreen mode

... e às expressões de funções

console.log(soma(20, 30)); // TypeError: soma is not a function
var soma = function (a, b) {
  return a + b;
}
Enter fullscreen mode Exit fullscreen mode

O que aconteceu? O motor do JS entendeu que a variável soma foi criada primeiro (com valor indefinido) e que só recebeu o seu "valor" (nesse caso, a função) depois. Por isso a mensagem de erro. Como const e let são bem mais limitados, vamos ver o que acontece:

console.log(soma(20, 30)); // ReferenceError: Cannot access 'soma' before initialization
let soma = function (a, b) {
  return a + b;
}
Enter fullscreen mode Exit fullscreen mode
console.log(soma(20, 30)); // ReferenceError: Cannot access 'soma' before initialization
const soma = function (a, b) {
  return a + b;
}
Enter fullscreen mode Exit fullscreen mode

Acontece exatamente como no caso das variáveis. Essas expressões também não podem ser acessadas antes da inicialização.

E quanto às funções declarativas?!

O comportamento para as funções declarativas será o mesmo das variáveis com var, só que nesse caso vai mostrar o valor declarado na função:

console.log(soma(20, 30)); // 50

function soma(a, b) {
  return a + b;
}
Enter fullscreen mode Exit fullscreen mode

E aí? Gostaram? Até a próxima anotação.

Discussion (0)