var
Variáveis em JavaScript são escopo de função. Isto é diferente de muitas outras linguagens (C # / Java etc.) onde as variáveis são escopo de bloco. Se você trouxer uma mentalidade escopo de bloco para JavaScript, você esperaria que o seguinte imprima 123
, em vez disso, ele irá imprimir 456
:
var foo = 123;
if (true) {
var foo = 456;
}
console.log(foo); // 456
Isto é porque {
não cria um novo escopo de variável. A variável foo
é a mesma dentro do bloco if, pois está fora do bloco if. Esta é uma fonte comum de erros na programação JavaScript. É por isso que o TypeScript (e o ES6) introduz a palavra-chave let
para permitir que você defina variáveis com verdadeiro escopo de bloco. Isto é, se você usar let
ao invés devar
, você terá um elemento verdadeiramente único desconectado do que você pode ter definido fora do escopo. O mesmo exemplo é demonstrado com let
:
let foo = 123;
if (true) {
let foo = 456;
}
console.log(foo); // 123
Outro lugar onde let
salvaria você de erros é em loops.
var index = 0;
var array = [1, 2, 3];
for (let index = 0; index < array.length; index++) {
console.log(array[index]);
}
console.log(index); // 0
Com toda a sinceridade, achamos melhor usar let
sempre que possível, pois isso leva a menores surpresas para desenvolvedores multilíngues novos e existentes.
Desde que mencionamos, gostaríamos de demonstrar que as funções criam um novo escopo de variável em JavaScript. Considere o seguinte:
var foo = 123;
function test() {
var foo = 456;
}
test();
console.log(foo); // 123
Isso se comporta como você esperaria. Sem isso, seria muito difícil escrever código em JavaScript.
O JS gerado pelo TypeScript é simples renomear a variável let
se um nome similar já existir no escopo ao redor. O seguinte exemplo é gerado como está com uma simples substituição de var
porlet
:
if (true) {
let foo = 123;
}
// torna-se //
if (true) {
var foo = 123;
}
No entanto, se o nome da variável já tiver sido usado pelo escopo ao redor, um novo nome de variável será gerado, conforme mostrado (aviso foo_1
):
var foo = '123';
if (true) {
let foo = 123;
}
// torna-se //
var foo = '123';
if (true) {
var foo_1 = 123; // renomeado
}
Você pode encapsular seus corpos case
em {}
para reutilizar nomes de variáveis de forma confiável em diferentes declarações case
como mostrado abaixo:
switch (name) {
case 'x': {
let x = 5;
// ...
break;
}
case 'y': {
let x = 10;
// ...
break;
}
}
Uma questão comum em uma entrevista de programação para um desenvolvedor de JavaScript é o que é o log desse arquivo simples:
var funcs = [];
// cria um monte de funções
for (var i = 0; i < 3; i++) {
funcs.push(function() {
console.log(i);
})
}
// chama-as
for (var j = 0; j < 3; j++) {
funcs[j]();
}
Seria de esperar que fosse "0,1,2". Surpreendentemente, ele será 3
para todas as três funções. A razão é que todas as três funções estão usando a variável i
do escopo externo e no momento em que as executamos (no segundo loop) o valor de i
será 3
(essa é a condição de terminação para o primeiro loop).
Uma correção seria criar uma nova variável em cada loop específico para essa iteração de loop. Como aprendemos antes, podemos criar um novo escopo de variável criando uma nova função e imediatamente a executando (isto é, o padrão IIFE das classes (function () {/*body*/})();
) como mostrado abaixo:
var funcs = [];
// cria um monte de funções
for (var i = 0; i < 3; i++) {
(function() {
var local = i;
funcs.push(function() {
console.log(local);
})
})();
}
// chama-as
for (var j = 0; j < 3; j++) {
funcs[j]();
}
Aqui as funções se fecham (portanto chamada closure
) da variável local (convenientemente chamada local
) e usam isso em vez da variável de laço i
.
Observe que os fechamentos vêm com um impacto no desempenho (eles precisam armazenar o estado ao redor).
A palavra-chave ES6 let
em um loop teria o mesmo comportamento do exemplo anterior:
var funcs = [];
// criar um monte de funções
for (let i = 0; i < 3; i++) { // Observe o uso do let
funcs.push(function() {
console.log(i);
})
}
// chamando-as
for (var j = 0; j < 3; j++) {
funcs[j]();
}
Usando um let
em vez de var
cria uma variável i
exclusiva para cada iteração de loop.
let
é extremamente útil para a grande maioria dos códigos. Ele pode melhorar muito a legibilidade do código e diminuir a chance de um erro de programação.