Você está ciente de que em JavaScript podemos adicionar { }
adicionar um nível de escopo onde quisermos?
Portanto, podemos sempre fazer o seguinte:
Incluí esse detalhe para garantir que os próximos exemplos façam sentido (já que não queria presumir que todos soubessem).
Antes do ES6, não havia outra maneira de declarar variáveis além var
. Mas ES6 nos trouxe let
e const
.
let
e const
declarações têm escopo de bloco, o que significa que só podem ser acessadas dentro do {
}
cercando-os. var
, por outro lado, não tem essa restrição.
Aqui está um exemplo:
O anterior ocorreu porque a nova declaração de babyAge
a 2 está disponível apenas dentro do if
quadra. Além disso, o primeiro babyAge
é usado. Você pode ver que são duas variáveis diferentes?
Em contraste, o var
declaração não tem escopo de bloco:
A diferença saliente final entre let
/ const
e var
é isso se você acessar var
antes de ser declarado, ele é indefinido. Mas se você fizer o mesmo por let
e const
, eles jogam um ReferenceError
.
console.log(varNumber); // undefinedconsole.log(letNumber); // Doesn't log, as it throws a ReferenceError letNumber is not definedvar varNumber = 1;let letNumber = 1;
Eles lançam o erro tudo por causa da Zona Morta Temporal.
Explicação da Zona Morta Temporal
Isso é o que o TDZ é: o termo para descrever o estado em que as variáveis são inacessíveis. Eles estão no escopo, mas não são declarados.
o let
e const
as variáveis existem no TDZ desde o início de seu escopo interno até que sejam declaradas.
Você também poderia dizer que as variáveis existem no TDZ desde o lugar em que foram vinculadas (quando a variável é vinculada ao escopo em que está) até que seja declarada (quando um nome é reservado na memória para essa variável).
Você pode ver acima que se eu acessasse a variável de idade antes de sua declaração, ele geraria um ReferenceError
. Por causa do TDZ.
Mas var
não vai fazer isso. var
é apenas inicializado por padrão para undefined
ao contrário da outra declaração.
Qual é a diferença entre declarar e inicializar?
Aqui está um exemplo de declaração de uma variável e inicialização de uma variável.
Declarar uma variável significa que reservamos o nome na memória no escopo atual. Isso é marcado como 1 nos comentários.
Inicializar uma variável é definir o valor da variável. Isso é marcado como 2 nos comentários.
Ou você sempre pode fazer ambos em uma linha. Isso é rotulado como 3 nos comentários.
Só para me repetir: o let
e const
as variáveis existem no TDZ desde o início de seu escopo interno até que sejam declaradas.
Portanto, a partir do trecho de código acima, onde está o TDZ para age
? Além disso, hands
tem um TDZ? Em caso afirmativo, onde é o início e o fim do TDZ para as mãos?
Verifique sua resposta
As variáveis mãos e idade entram no TDZ.
O TDZ para mãos termina quando é declarado, a mesma linha que é definida como 2.
O TZ para idade termina quando é declarado, e o nome é reservado na memória (na etapa 2, onde comentei).
Por que o TDZ é criado quando é?
Vamos voltar ao nosso primeiro exemplo:
{ // This is the temporal dead zone for the age variable! // This is the temporal dead zone for the age variable! // This is the temporal dead zone for the age variable! // This is the temporal dead zone for the age variable! let age = 25; // Whew, we got there! No more TDZ console.log(age);}
Se adicionarmos um console.log
dentro do TDZ você verá este erro:
Por que o TDZ existe entre o topo do escopo e a declaração da variável? Qual é a razão específica para isso?
É por causa do içamento.
O mecanismo JS que analisa e executa seu código tem 2 etapas a seguir:
- Análise do código em uma árvore de sintaxe abstrata / código de byte executável e
- Execução em tempo de execução.
A etapa 1 é onde o içamento acontece, e isso é feito pelo mecanismo JS. Basicamente, ele moverá todas as suas declarações de variáveis para o topo de seu escopo. Portanto, um exemplo seria:
console.log(hoistedVariable); // undefinedvar hoistedVariable = 1;
Para ser claro, essas variáveis não estão se movendo fisicamente no código. Mas, o resultado seria funcionalmente idêntico ao abaixo:
var hoistedVariable;console.log(hoistedVariable); // undefinedcounter = 1;
A única diferença entre const
e let
é que quando eles são içados, seus valores não são padronizados para undefined
.
Só para provar let
e const
também içar, aqui está um exemplo:
{ // Both the below variables will be hoisted to the top of their scope! console.log(typeof nonsenseThatDoesntExist); // Prints undefined console.log(typeof name); // Throws an error, cannot access 'name' before initialization let name = "Kealan";}
O snippet acima é a prova de que let
está claramente içado acima de onde foi declarado, pois o motor nos alerta para o fato. Sabe name
existe (é declarado), mas não podemos acessá-lo antes de ser inicializado.
Se ajudar você a se lembrar, pense assim.
Quando as variáveis são levantadas, var
pega undefined
inicializado com seu valor por padrão no processo de içamento. let
e const
também são içados, mas não configurados para undefined
quando eles são içados.
E essa é a única razão pela qual temos o TDZ. É por isso que acontece com let
e const
mas não var
.
Mais exemplos do TDZ
O TDZ também pode ser criado para parâmetros de função padrão. Então, algo assim:
function createTDZ(a=b, b) {}createTDZ(undefined, 1);
joga um ReferenceError
, porque a avaliação da variável a
tenta acessar a variável b
antes de ser analisado pelo mecanismo JS. Os argumentos da função estão todos dentro do TDZ até que sejam analisados.
Mesmo algo tão simples como let tdzTest = tdzTest;
lançaria um erro devido ao TDZ. Mas var
aqui seria apenas criar tdzTest
e configurá-lo para undefined
.
Há mais uma final e exemplo bastante avançado de Erik Arvindson (que está envolvido na evolução e manutenção da especificação ECMAScript):
Você pode seguir os números comentados.
Na primeira linha chamamos de f
função e, em seguida, tente acessar o b
variável (que lança um ReferenceError
Porque b
está no TDZ).
Por que temos o TDZ?
Dr. Alex Rauschmayer tem um excelente postar em porque o TDZ existe, e o principal motivo é este:
Isso nos ajuda a detectar erros.
Tentar acessar uma variável antes de ser declarada é o caminho errado e não deveria ser possível.
Ele também fornece uma semântica mais esperada e racional para const
(Porque const
é içado, o que acontece se um programador tentar usá-lo antes de ser declarado em tempo de execução? Qual variável deve ser mantida no ponto em que é içada?), E foi a melhor abordagem decidida pela equipe de especificações ECMAScript.
Como evitar os problemas que o TDZ causa
De forma relativamente simples, sempre certifique-se de definir seu let
areia const
s no topo de seu escopo.
Você pode ler mais sobre este tópico nas seguintes postagens:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#Temporal_dead_zone
https://exploringjs.com/es6/ch_variables.html#sec_temporal-dead-zone
https://ponyfoo.com/articles/es6-let-const-and-temporal-dead-zone-in-depth