Hoisting: Que bruxaria é essa?!
Tags: hoisting, var, let, const
Published: 2021-08-16 20:52:36

##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.

{% link https://dev.to/ananopaisdojavascript/var-let-e-const-qual-e-a-diferenca-473o %}

##Tentando entender...

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

```javascript
console.log(a); // ReferenceError: a is not defined
```

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?!

```javascript
console.log(a); // undefined
var a;
```

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.

```javascript
var a;
console.log(a); // undefined
```

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

```javascript
console.log(a); // undefined
var a = 20;
```

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

```javascript
var a;
console.log(a); // undefined
a = 20;
```

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?

```javascript
console.log(a); // undefined
var a = 20;
console.log(a); // 20
```

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?!

```javascript
console.log(a); // ReferenceError: Cannot access 'a' before initialization
let a;
```

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?!

```javascript
console.log(a); // ReferenceError: Cannot access 'a' before initialization
let a = 123;
console.log(a); // ReferenceError: Cannot access 'a' before initialization
```

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`...

```javascript
console.log(a); // ReferenceError: Cannot access 'a' before initialization
const a = 123;
```

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

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

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:

```javascript
console.log(soma(20, 30)); // ReferenceError: Cannot access 'soma' before initialization
let soma = function (a, b) {
  return a + b;
}
```

```javascript
console.log(soma(20, 30)); // ReferenceError: Cannot access 'soma' before initialization
const soma = function (a, b) {
  return a + b;
}
```

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:

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

function soma(a, b) {
  return a + b;
}
```

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