<devzera

Aprendendo e compartilhando tecnologia

Um guia para entender JavaScript hoisting 🚩com variáveis usando let e const

Esse artigo é uma livre tradução do artigoA guide to JavaScript variable hoisting 🚩 with let and const, porBhuvan Malik

Novos desenvolvedores de JavaScript geralmente têm dificuldade em entender o comportamento exclusivo do hoisting devariáveis​​/funções.

Já que vamos falar sobre declarações devar, leteconstmais tarde, é importante entender o içamento (hoisting) de variáveis ​​em vez do içamento (hoisting) de funções. Vamos mergulhar!

O que é içamento de variável (variable hoisting)?

A engine do Javascript trata todas as variáveis declaradas comvarcomo se fossem declaradas no topo do escopo de uma função (se colocadas dentro de uma), ou no topo do escopo global (se declaradas fora de uma função), independentemente de onde a declaração real ocorrer. Isso essencialmente é “hoisting”.

Então, variáveis podem realmente estar disponíveis antes de sua declaração.

Hoisting

Vamos ver essa parada em ação…

// Saída (Output): undefined
console.log(shape);

var shape = "square";

// Saída (Output): "square"
console.log(shape);

Se você está vindo de linguagens baseadas em C, você estava esperando que um erro fosse lançado ao chamar o primeiroconsole.log, já que a variávelshapenão foi definida naquele momento. Entretanto, o interpretador do Javascript vai além, e iça (hoists) todas as declarações de variáveis pro top, e a suas inicializações permanecem no mesmo local.

É isso que acontece por baixo dos panos:

//a declaraçã da variável é içada (hoisted)
var shape;

// Saída (Output): undefined
console.log(shape);

shape = "square";

// Saída (Output): "square"
console.log(shape);

Abaixo está outro exemplo, dessa vez no escopo de uma função para deixar as coisas mais claras:

function getShape(condition) {
// shape existe aqui com o valor "undefined"
// Saída (Output): undefined
console.log(shape);

if (condition) {
        var shape = "square";
        // outro código qualquer
        return shape;
    } else {
        // shape existe aqui com o valor "undefined"
        return false;
    }
}

Perceba que no exemplo acima a declaração deshapeé içada (hoisted) para o topo da funçãogetShape. Isso acontece pois os blocosif/elsenão criam escopos locais como vemos em outras linguagens. O escopo local é essencialmente o escopo de um função em JavaScript. Portanto, “shape” é acessível em todos os lugares fora do bloco if e dentro da função com um valor “undefined”.

Esse comportamento padrão do JavaScript tem suas vantagens e desvantagens. Não entender completamente isso pode levar a bugs sutis, mas perigosos, em nosso código.

Declarações no Nível do Bloco (Block-Level Declarations)

OES6introduziu escopos no nível de bloco (block-level scoping) para prover aos desenvolvedores maior controle e flexibilidade sobre o ciclo de vida de uma variável.

Declarações no Nível do Bloco (Block-Level Declarations) são feitas em blocks/escopos léxicos que são criadas dentro do block{}.

Declaração com “let”

Essa syntax é similar avar, apenas troquevarporletpara declarar uma variável para que seu escopo fique apenas naquele bloco.

Coloque a declaração do seuletno topo do bloco para que ele esteja disponível para o bloco inteiro.

Exemplo:

function getShape(condition) {
// shape não existe aqui
// console.log(shape); => ReferenceError: shape is not defined
if (condition) {
        let shape = "square";
        // algum có
        return shape;
    } else {
        // shape também não existe
        return false;
    }
}

Perceba comoshapeexiste apenas dentro do block doif, e como um erro é lançado quando é feita a tentiva de acesso for dele, lançando umundefinedcomo vimos anteriormente quando usamosvar.

Observação:Se um identificador já foi definido dentro do escopo comvar, usando o mesmo identificador comoletdentro desse escopo lançará um erro. Além disso, nenhum erro será mostrado se uma declaraçãoletcriar uma variável com o mesmo nome de uma variável em seu escopo externo. (Este caso é o mesmo ao usarconst).

Por exemplo:

var shape = "square";

let shape = "rectangle";

// SyntaxError: Identifier 'shape' has already been declared

e:

var shape = "square";

if (condicao) {
    // não lança um erro
    let shape = "rectangle";
    // mais código
}

// Sem erro

Declaração com “const”

A syntax dessa declaração é similar alet&var, o ciclo de vida(lifecycle) é o mesmo que o dolet, mas você precisa seguir certas regras.

Todaconsté tratada comoconstantes, e, portanto, ela não pode ter seu valore reatribuído após ser definida. Devido a isto, todaconstdeve ser inicializada no momento da declaração.

Exemplo:

// válido 
const shape = "triangle";
// syntax error: missing initialization

const color;
// TypeError: Assignment to constant variable

shape = "square"

Entretando, propriedades de um objeto podem ser alteradas!

const shape = {
    name: "triangle",
    sides: 3
}

// FUNCIONA
shape.name = "square";
shape.sides = 4;

// SyntaxError: Invalid shorthand property initializer
shape = {
    name: "hexagon",
    sides: 6
}

No exemplo acima podemos ver que as propriedades do objetoshapepuderem ser alteradas pois nós mudamos apenas o que ela contém, e não o que está vinculado, como em uma string, por exemplo.

Podemos resumir dizendo queconstimpede a modificação da ligação(binding) como um todo — não o valor ao qual ela está vinculada.

Observação: Propriedades podem ser alteradas. Para uma real imutabilidade user Object.Freeze, Immutable.js ou Mori.

A zona temporal morta

Agora sabemos que acessar uma variável comletouconstantes de ser declarada lançará umReferenceError. Esse período entre a entrada do escopo e a declaração de onde eles não podem ser acessados é chamado de Zona Temporal Morta (Temporal Dead Zone).

Perceba que a “Zona Temporal Morta” não é formalmente mencionada nas especificações do ECMAScript, é apenas um termo popular entre os programadores.

Eu pessoalmente recomendo que sempre useconst, pois gera menos bugs. Atualmente eu raramente encontro uma situação onde precise usar ovar.

Como regra geral, useletsomente para contadores de loop ou se você realmente precisará alterar o valor da variável depois. Pra qualquer outro caso, vá deconst. Pessoalmente eu abandonei loops para usar filter (), map () & reduce (). Você deveria também.

Fica esperto e veja a 2a parte em “Function Hoisting & Questões importantes de hoisting em processos seletivos”.

https://medium.freecodecamp.org/function-hoisting-hoisting-interview-questions-b6f91dbc2be8

Cliqueaquipara ver meus artigos sobre as úteis features do ES6 relacionadas a funções.

Comentários