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 de*variáveis*/funções.
Já que vamos falar sobre declarações devar, let
econst
mais 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 comvar
como 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.
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ávelshape
nã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/else
nã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)
O*ES6*introduziu 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 troquevar
porlet
para declarar uma variável para que seu escopo fique apenas naquele bloco.
Coloque a declaração do seulet
no 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 comoshape
existe apenas dentro do block doif
, e como um erro é lançado quando é feita a tentiva de acesso for dele, lançando umundefined
como vimos anteriormente quando usamosvar
.
Observação:Se um identificador já foi definido dentro do escopo comvar
, usando o mesmo identificador comolet
dentro desse escopo lançará um erro. Além disso, nenhum erro será mostrado se uma declaraçãolet
criar 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 como*constantes*, e, portanto, ela não pode ter seu valore reatribuído após ser definida. Devido a isto, todaconst
deve 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 objetoshape
puderem 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 queconst
impede 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 comlet
ouconst
antes 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, uselet
somente 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.
Top comments (0)