Var, let ou const? Problemas de escopo variável e ES6

Os escopos no JavaScript sempre foram um tópico complicado, especialmente quando comparado a linguagens mais estritamente organizadas, como C e Java. Por muitos anos, o escopo em JS não foi amplamente discutido, uma vez que a linguagem simplesmente não tinha meios que afetassem significativamente a situação atual. Porém, no ECMAScript 6, existem alguns novos recursos que permitem aos desenvolvedores controlar melhor o escopo das variáveis. Agora, esses recursos são muito bem suportados pelos navegadores, eles são bastante acessíveis para a maioria dos desenvolvedores. No entanto, novas palavras-chave para declarar variáveis, levando em consideração o fato de a antiga palavra-chave var não ter desaparecido, significam não apenas novas oportunidades, mas também o surgimento de novas perguntas. Quando usar as palavras-chave let e const ? Como eles se comportam? Em que situações a palavra-chave var ainda é relevante? O material, cuja tradução publicamos hoje, visa explorar o problema do escopo das variáveis ​​em JavaScript.



Escopos variáveis: uma visão geral


O escopo de uma variável é um conceito importante em programação, que, no entanto, pode confundir alguns desenvolvedores, especialmente iniciantes. O escopo de uma variável é a parte do programa em que essa variável pode ser acessada.

Veja o seguinte exemplo:

 var myVar = 1; function setMyVar() { myVar = 2; } setMyVar(); console.log(myVar); 

Qual será o resultado do método console.log ? A resposta a esta pergunta não surpreenderá ninguém: ela produzirá 2 . A variável myVar declarada fora de uma função, que nos diz que é declarada no escopo global. Portanto, qualquer função declarada no mesmo escopo poderá acessar myVar . De fato, quando se trata de código executado em um navegador, mesmo as funções declaradas em outros arquivos conectados à página terão acesso a essa variável.

Agora dê uma olhada no seguinte código:

 function setMyVar() { var myVar = 2; } setMyVar(); console.log(myVar); 

Externamente, suas alterações, comparadas com o exemplo anterior, são insignificantes. Ou seja, apenas colocamos a declaração da variável dentro da função. O que o console.log produzirá agora? De fato, nada, já que essa variável não é declarada e quando você tenta acessá-la, uma mensagem sobre um erro não processado de ReferenceError será exibida. Isso aconteceu porque a variável foi declarada dentro da função usando a palavra-chave var . Como resultado, o escopo dessa variável é limitado ao escopo interno da função. Ele pode ser acessado no corpo desta função, as funções incorporadas nessa função podem funcionar com ela, mas não é acessível a partir do exterior. Se precisarmos de várias funções no mesmo nível para usar uma determinada variável, precisamos declarar essa variável no mesmo local em que essas funções são declaradas, ou seja, um nível superior ao seu escopo interno.

Aqui está uma observação interessante: o código da maioria dos sites e aplicativos da web não se aplica ao trabalho de nenhum programador. A maioria dos projetos de software é resultado do desenvolvimento da equipe e, além disso, eles usam bibliotecas e estruturas de terceiros. Mesmo que apenas um programador esteja envolvido no desenvolvimento de um site, ele geralmente usa recursos externos. Por esse motivo, geralmente não é recomendável declarar variáveis ​​no escopo global, pois você não pode saber antecipadamente quais variáveis ​​serão declaradas por outros desenvolvedores cujo código será usado no projeto. Para contornar esse problema, você pode usar alguns truques, em particular o padrão " Módulo " e o IIFE ao aplicar a abordagem orientada a objetos para o desenvolvimento de JavaScript, embora o encapsulamento de dados e funções em objetos comuns possa obter o mesmo efeito. Em geral, pode-se notar que variáveis ​​cujo escopo vai além do que precisam geralmente são um problema com o qual algo precisa ser feito.

Problema da palavra-chave Var


Então, descobrimos o conceito de "escopo". Agora vamos para coisas mais complexas. Dê uma olhada no seguinte código:

 function varTest() { for (var i = 0; i < 3; i++) {   console.log(i); } console.log(i); } varTest(); 

O que chegará ao console após sua execução? É claro que os valores do contador crescente i : 0 , 1 e 2 serão exibidos dentro do loop. Depois que o loop termina, o programa continua em execução. Agora, estamos tentando acessar a mesma variável de contador que foi declarada no loop for , fora deste loop. O que virá disso?

Depois de chamar i fora do loop, 3 entrará no console, pois a palavra-chave var atua no nível da função. Se você declarar uma variável usando var , poderá acessá-la em uma função mesmo após sair da construção em que foi declarada.

Isso pode se transformar em um problema quando as funções se tornam mais complexas. Considere o seguinte exemplo:

 function doSomething() { var myVar = 1; if (true) {   var myVar = 2;   console.log(myVar); } console.log(myVar); } doSomething(); 

O que chegará ao console agora? 2 e 2 . Declaramos uma variável, inicializamos com o número 1 e tentamos redefinir a mesma variável dentro da if . Como essas duas declarações existem no mesmo escopo, não podemos declarar uma nova variável com o mesmo nome, mesmo que obviamente desejemos fazer exatamente isso. Como resultado, a primeira variável é substituída dentro da if .

Essa é precisamente a maior falha da palavra-chave var . O escopo das variáveis ​​declaradas usando-o é muito grande. Isso pode levar à substituição acidental de dados e outros erros. Grandes áreas de visibilidade geralmente levam a programas imprecisos. Em geral, uma variável deve ter um escopo limitado por suas necessidades, mas não as excedendo. Seria bom poder declarar variáveis ​​cujo escopo não é tão grande quanto ao usar var , o que tornaria possível, se necessário, usar construções de software mais estáveis ​​e melhores à prova de erros. Na verdade, o ECMAScript 6 nos oferece essas oportunidades.

Novas maneiras de declarar variáveis


O padrão ECMAScript 6 (um novo conjunto de recursos JavaScript, também conhecido como ES6 e ES2015) fornece duas novas maneiras de declarar variáveis ​​que diferem em escopo, em comparação com var , e têm mais alguns recursos. Estas são as palavras-chave let e const . Ambos nos dão o chamado escopo de bloco. Isso significa que o escopo de seu uso pode ser limitado a um bloco de código, como um loop for ou uma if . Isso dá ao desenvolvedor mais flexibilidade na escolha do escopo das variáveis. Considere as novas palavras-chave.

▍Utilização da palavra-chave let


A palavra-chave let é muito semelhante à var , a principal diferença é o escopo limitado das variáveis ​​declaradas com ela. Reescrevemos um dos exemplos acima, substituindo var por let :

 function doSomething() { let myVar = 1; if (true) {   let myVar = 2;   console.log(myVar); } console.log(myVar); } doSomething(); 

Nesse caso, os números 2 e 1 chegarão ao console. Isso acontece porque a if define um novo escopo para a variável declarada com a palavra-chave let . Isso leva ao fato de que a segunda variável declarada é uma entidade completamente independente, não relacionada à primeira. Você pode trabalhar com eles independentemente um do outro. No entanto, isso não significa que blocos de código aninhados, como a nossa if , sejam completamente cortados das variáveis ​​declaradas com a palavra-chave let no escopo em que elas mesmas estão localizadas. Dê uma olhada no seguinte código:

 function doSomething() { let myVar = 1; if (true) {   console.log(myVar); } } doSomething(); 

Neste exemplo, o console receberá o número 1 . O código dentro da if tem acesso à variável que criamos fora dela. Portanto, ele exibe seu valor no console. E o que acontece se você tentar misturar o escopo? Por exemplo, faça o seguinte:

 function doSomething() { let myVar = 1; if (true) {   console.log(myVar);   let myVar = 2;   console.log(myVar); } } doSomething(); 

Pode parecer que a primeira chamada console.log produzirá 1 , mas, de fato, quando você tentar executar esse código, um erro ReferenceError aparecerá myVar que a variável myVar para esse escopo não está definida ou não inicializada (o texto desse erro difere em diferentes navegadores). No JavaScript, existe o aumento de variáveis ​​para o topo de seu escopo. Ou seja, se uma variável é declarada em um determinado escopo, o JavaScript reserva um lugar para ela antes mesmo que o comando declare que ela é executada. Como exatamente isso acontece difere ao usar var e let .

Considere o seguinte exemplo:

 console.log(varTest); var varTest = 1; console.log(letTest); let letTest = 2; 

Nos dois casos, tentamos usar a variável antes de declará-la. Mas os comandos de saída do console se comportam de maneira diferente. O primeiro, usando uma variável que posteriormente será declarada usando a palavra-chave var , produzirá undefined - ou seja, o que será gravado nessa variável. O segundo comando, que tenta acessar a variável, que mais tarde será declarada usando a palavra-chave let , lançará um ReferenceError e nos dirá que estamos tentando usar a variável antes que ela seja declarada ou inicializada. Qual é o problema?

Mas o fato é que, antes que o código seja executado, os mecanismos responsáveis ​​por sua execução examinam esse código, descobrem se alguma variável será declarada nele e, nesse caso, aumentam-na com reserva de espaço para elas. Nesse caso, as variáveis ​​declaradas usando a palavra-chave var são inicializadas para undefined em seu escopo, mesmo se forem acessadas antes de serem declaradas. O principal problema aqui é que o valor undefined em uma variável nem sempre indica que eles estão tentando usar a variável antes de sua declaração. Veja o seguinte exemplo:

 var var1; console.log(var1); console.log(var2); var var2 = 1; 

Nesse caso, embora var1 e var2 declarados de maneira diferente, ambas as chamadas para console.log produzirão undefined . O ponto aqui é que, nas variáveis ​​declaradas com var , mas não inicializadas, o valor undefined gravado automaticamente. Ao mesmo tempo, como já dissemos, as variáveis ​​declaradas usando var , que são acessadas antes de serem declaradas, também contêm undefined . Como resultado, se algo der errado nesse código, não será possível entender qual é exatamente a origem do erro - usando uma variável não inicializada ou usando uma variável antes de sua declaração.

O local para variáveis ​​declaradas com a palavra-chave let é reservado em seu bloco, mas, antes de serem declaradas, elas caem na zona morta temporária (TDZ, Zona morta temporária). Isso leva ao fato de que eles não podem ser usados ​​antes de serem declarados, e uma tentativa de acessar essa variável leva a um erro. No entanto, o sistema sabe exatamente a causa do problema e o reporta. Isso é visto claramente neste exemplo:

 let var1; console.log(var1); console.log(var2); let var2 = 1; 

Aqui, a primeira chamada para console.log será console.log undefined , e a segunda gerará um erro ReferenceError , informando que a variável ainda não foi declarada ou inicializada.

Como resultado, se o uso de var parecer undefined , não sabemos o motivo desse comportamento do programa. Uma variável pode ser declarada e não inicializada, ou ainda não pode ser declarada neste escopo, mas será declarada no código localizado abaixo do comando para acessá-la. Usando a palavra-chave let , podemos entender exatamente o que está acontecendo, o que é muito mais útil para depuração.

▍Utilização da palavra-chave const


A palavra-chave const é muito parecida com let , mas eles têm uma diferença importante. Esta palavra-chave é usada para declarar constantes. Os valores das constantes não podem ser alterados após sua inicialização. Note-se que isso se aplica apenas a valores de tipos primitivos, água, seqüências de caracteres ou números. Se a constante é algo mais complexo, por exemplo, um objeto ou uma matriz, a estrutura interna de uma entidade pode ser modificada, você não pode simplesmente substituí-la por outra. Dê uma olhada no seguinte código:

 let mutableVar = 1; const immutableVar = 2; mutableVar = 3; immutableVar = 4; 

Este código será executado até a última linha. Tentar atribuir um novo valor a uma constante resultará em um erro de TypeError . É assim que as constantes se comportam, mas, como já mencionado, os objetos inicializados pelas constantes podem ser alterados, eles podem sofrer mutações, o que pode levar a surpresas .

Talvez você, como desenvolvedor de JavaScript, esteja se perguntando por que a imunidade de variáveis ​​é importante. As constantes são um fenômeno novo no JavaScript, enquanto são parte essencial de linguagens como C ou Java. Por que esse conceito é tão popular? O fato é que o uso de constantes nos faz pensar em como nosso código funciona. Em algumas situações, alterar o valor de uma variável pode interromper o código, por exemplo, se o número Pi estiver escrito nela e for constantemente acessado ou se a variável tiver um link para um elemento HTML com o qual você precisa trabalhar constantemente. Digamos, aqui está uma constante na qual um link para um determinado botão é gravado:

 const myButton = document.querySelector('#my-button'); 

Se o código depende do link para o elemento HTML, precisamos garantir a imutabilidade desse link. Como resultado, podemos dizer que a palavra-chave const segue não apenas o caminho das melhorias no campo de visibilidade, mas também o caminho de limitar a possibilidade de modificar os valores das constantes declaradas usando essa palavra-chave. Lembre-se de como dissemos que uma variável deveria ter exatamente o escopo necessário. Essa idéia pode ser continuada apresentando uma recomendação, segundo a qual uma variável deve ter apenas a capacidade de mudar, necessária para o trabalho adequado com ela e nada mais. Aqui está um bom material sobre o tema da imunidade, do qual se pode tirar uma conclusão importante, segundo a qual o uso de variáveis ​​imutáveis ​​nos faz pensar com mais cuidado sobre nosso código, o que leva a uma melhoria na pureza do código e a uma redução no número de surpresas desagradáveis ​​decorrentes de sua operação.

Quando comecei a usar as palavras-chave let e const , basicamente usei let , recorrendo a const apenas ao escrever um novo valor em uma variável declarada com let poderia prejudicar o programa. Mas, aprendendo mais sobre programação, mudei de idéia sobre isso. Agora minha ferramenta principal é const , e let me usá-la somente quando o valor da variável precisar ser reescrito. Isso me faz pensar se é realmente necessário alterar o valor de uma determinada variável. Na maioria dos casos, isso não é necessário.

Precisamos da palavra-chave var?


As palavras-chave let e const contribuem para uma abordagem de programação mais responsável. Existem situações em que a palavra-chave var ainda é necessária? Sim existem. Existem várias situações em que essa palavra-chave ainda é útil para nós. Considere com cuidado o que falaremos antes de alterar var para let ou const .

▍ Var nível de suporte de palavras-chave pelos navegadores


Variáveis ​​declaradas com a palavra-chave var têm um recurso muito importante que let e const falta. Ou seja, estamos falando sobre o fato de que absolutamente todos os navegadores suportam essa palavra-chave. Embora o suporte para let e const pelos navegadores seja muito bom, existe o risco de o seu programa terminar em um navegador que não os suporta. Para entender as consequências de um incidente desse tipo, você precisa considerar como os navegadores lidam com o código JavaScript não suportado, em vez de, por exemplo, como eles reagem ao código CSS que eles não entendem.

Se o navegador não suportar algum recurso de CSS, isso basicamente causará alguma distorção do que será exibido na tela. Um site em um navegador que não suporta nenhum dos estilos usados ​​pelo site não terá a aparência esperada, mas provavelmente poderá ser usado. Se você usar, por exemplo, let , e o navegador não suportar essa palavra-chave, seu código JS simplesmente não funcionará lá. Não será - isso é tudo. Como o JavaScript é um dos componentes importantes da Web moderna, isso pode se tornar um problema sério se você precisar que seus programas funcionem em navegadores desatualizados.

Quando as pessoas falam sobre o suporte a navegadores de sites, geralmente perguntam em qual navegador o site funcionará de maneira ideal. Se estamos falando de um site cuja funcionalidade é baseada no uso de let e const , uma pergunta semelhante terá que ser colocada de maneira diferente: “Em quais navegadores nosso site não funcionará?”. E isso é muito mais sério do que falar sobre o uso da display: flex ou não. Para a maioria dos sites, o número de usuários com navegadores desatualizados não será grande o suficiente para se preocupar. No entanto, se estamos falando de algo como uma loja on-line ou sites cujos proprietários compram publicidade, isso pode ser uma consideração muito importante. Antes de usar novas oportunidades em tais projetos, avalie o nível de risco.

Se você precisa oferecer suporte a navegadores realmente antigos, mas deseja usar let , const e outros novos recursos do ES6, uma das soluções para esse problema é usar um transportador JavaScript como o Babel . Os transpilers fornecem a tradução de um novo código para o que os navegadores antigos entenderão. Usando o Babel, você pode escrever um código moderno que use os recursos mais recentes do idioma e depois convertê-lo em código que os navegadores mais antigos possam executar.

, ? , . , , , , . , . , , . ES6-, Babel, Babel , , . , , . . ? - IE8 ? , , , , , .

▍ var


, var , . . :

 var myVar = 1; function myFunction() { var myVar = 2; //  ,     myVar    ! } 

, myVar , , . , . , , , , . , . var .

 var myVar = 1; function myFunction() { var myVar = 2; console.log(myVar); // 2 console.log(window.myVar); // 1 } 

var , window . let const . , JS- , (, , ) , .

, . , . , , , :

 let myGlobalVars = {}; let myVar = 1; myGlobalVars.myVar = myVar; function myFunction() { let myVar = 2; console.log(myVar); // 2 console.log(myGlobalVars.myVar); // 1 } 

, , , , . , , var , , , , , .

Sumário


, ? ? :

  • IE10 - ? — var .
  • JavaScript, , , var , const . - (, , ) — let .

let const , ECMAScript 6, ( ) - -. , , , . , - , «» «» , , let const , .

! , const var , let , , , ?

Source: https://habr.com/ru/post/pt420359/


All Articles