Estou assistindo a prática do ES2015 recentemente, há um ditado que diz
Não há escopo no nível de bloco em JavaScript
Você pode não entender esse problema, vamos dar uma olhada em um exemplo primeiro
var a = [] para (var i = 0; i <10; i ++) {a [i] = function () {console.log (i); }} a [6] ();Eu acho que muitas pessoas pensam que o resultado dessa pergunta é 6, mas, infelizmente, a resposta é 10. Tente outra coisa. a [7] (), a [8] () e a [8] () todos têm 10 !!
Como o JS geralmente envolve as variáveis primitivas nos objetos correspondentes ao processamento, por exemplo, para var str = "hello world"; str.slice (1).
O processo real de JS é provavelmente var str = "Hello World"; New String (str) .Slice (1). Esse processo pode causar problemas para entendermos o problema.
Para explicar esse problema aqui, pertenço ao tipo de número no tipo primitivo, declaro explicitamente como o tipo de número. Como o processo de atribuição do tipo básico é aplicar a memória e modificar a direção da variável, também usamos o processo de novo objeto de número para simular esse processo. O código modificado é o seguinte:
var a = [] var i = novo número (0); para (; i <10; i = novo número (i+1)) {a [i] = function () {console.log (i.toString ()); }} a [6] (); // 10a [7] (); // 10a [8] (); // 10a [9] (); // 10a [9] (); // 10Vamos dar uma olhada nos endereços de memória relativa dessas variáveis em combinação com um programa.
(function () {var id = 0; function generateId () {return id ++;}; object.prototype.id = function () {var newid = generateId (); this.id = function () {return;}; retorna; novo número (i+1), i.id ()) {a [i] = function () {console.log (i.id ()); console.log (i.toString ()); }} a [6] (); // 10 10a [7] (); // 10 10a [8] (); // 10 10a [9] (); // 10 10console.log (i.id ()) // 10Aqui, de fato, simulamos todo o efeito de "atribuição" do nosso I e o endereço relativo de I Alteri de 0 para 10 (no final, ele precisa ser adicionado uma vez antes de poder sair do loop for).
Enquanto olhamos para o endereço relativo de I, encontramos um problema: quando a função correspondente a [x] (x: 0 ~ 9) é executada, o endereço relativo de I referenciado é 10. Por quê?
Aqui, envolveremos o problema do escopo no nível do bloco. Aqui citamos uma passagem de Ruan Yifeng na introdução ao ES6:
O ES5 possui apenas escopo global e escopo da função, mas nenhum escopo no nível do bloco.
O ES5 é a versão mais usada do JS. Esta frase diz que, em JavaScript, não há escopo de bloco. Existe apenas escopo global e escopo no nível do bloco.
Como entender? Por exemplo
for (var i = 0; i <10; i ++) {console.log (i);} console.log (i); // 10console.log (window.i); // 10Intuitivamente, achamos que o loop for um bloco de código e deve pertencer a um escopo em nível de bloco. No entanto, não apenas pode produzir 0 a 9 normalmente, mas também pode gerar 10 externamente no loop for. Ao mesmo tempo, descobrimos que, embora tenhamos definido i no loop for, parece que estou pendurado no objeto global da janela (se for o ambiente de execução do NodeJS, ele estará pendurado no objeto global)
Portanto, blocos como os loops em JavaScript não terão o efeito de um escopo no nível do bloco. A definição de variáveis em blocos de código, como para loops, não é diferente das variáveis diretamente definidas no escopo atual.
Mas podemos isolar o escopo por meio de funções:
(function () {for (var i = 0; i <10; i ++) {console.log (i);} console.log (i);}) () console.log (i); //// i não é definidoAo mesmo tempo, se console.log (window.i); é executado, o resultado indefinido será obtido. Aqui, usamos uma função de execução imediata para formar um escopo. Ele desempenha um papel semelhante a um bloco de código. Após esse escopo de função, a variável não posso mais ser acessada. No entanto, posso ser acessado a qualquer momento no escopo da função.
Volte para a pergunta anterior e, em seguida, a entenderemos em combinação apenas com o escopo global e o escopo no nível de bloco em JavaScript. No loop for, o eu definido deve ser definido no escopo atual, ou seja, o escopo da janela. No corpo do loop, atribuímos uma função a um [i]. Quando executamos essa função, a situação é a seguinte:
Eu não existo na função, então seguimos a cadeia de escopo para encontrar i. O i que somamos neste momento é este i. Como eu pulo para fora do último +1 do loop, eu me torna 10, então o resultado da saída é sempre 10. Mas o que realmente precisamos não é o último I, mas eu no processo intermediário. Se queremos resolver esse problema, precisamos deixar de lado a variável i (porque o último eu inevitavelmente me torna 10). Precisamos permitir que a função correspondente a [0] consulte o valor 0 e deixe a função correspondente a [1] consulte o valor 1. Como mostrado na figura abaixo:
Volte ao nosso código anterior.
A seta na figura mostra que podemos acessar i (0 ~ 9). Aqui, porque o loop for não forma um escopo de nível de bloco por si só, acessamos o que eu defini pelo loop for quando seguimos a corrente do escopo.
Aqui, embrulhamos nosso código com uma função de execução imediata, que pode formar um escopo, e passamos o valor que eu. A seguir:
var a = [] var i = novo número (0); console.log (i.id ()); // 0for (; i <10; i = novo número (i+1), i.id ()) {(function (i) {a [i] = function () {console.log (i.id ()); // 6 6a [7] (); // 7 7a [8] (); // 8 8a [9] (); // 9 9console.log (i.id ()); // 10}Como essa função de execução imediata refere -se ao valor numérico 0 ~ 9, quando executarmos a função A [i], primeiro encontraremos o escopo da função de execução imediata ao longo da cadeia de escopo. A função de execução imediata mantém a referência numérica de 0 ~ 9 e podemos emitir corretamente o valor de I na função a [i]. Através do resultado da execução, podemos ver que não apenas o resultado da execução está correto, mas o endereço de memória relativo do valor que referencia também está correto. Em seguida, alteramos o objeto numérico que foi originalmente declarado explicitamente para testar. Do seguinte modo:
var a = []; para (var i = 0; i <10; i ++) {(function (i) {a [i] = function () {console.log (i);}}) (i);}Por fim, vamos dar uma olhada na sintaxe ES6 recomendada usando LET em vez de var e compilar e gerar código ES5 através do Baable:
// código ES6 var a = [] para (vamos i = 0; i <10; i ++) {a [i] = function () {console.log (i); }} a [6] (); // Babel compilou e gerou código ES5 "Use Strict"; var a = []; var _loop = function _loop (i) {a [i] = function () {console.log (i); };}; para (var i = 0; i <10; i ++) {_loop (i);} a [6] ();Vamos ver se nossa solução é muito semelhante à solução ES6. Aqui, nossa função de execução imediata é equivalente à execução da função _loop e _loop (i) no código ES5 gerado.