No processo de aprendizagem front-end, inevitavelmente encontraremos muitos problemas, por isso hoje falaremos sobre duas questões do ponto de vista de um iniciante:
O que é um encerramento?
Quais são as funções dos fechamentos?
Na verdade, os encerramentos estão por toda parte quando aprendemos JavaScript, você só precisa ser capaz de reconhecê-los e aceitá-los. Closures não são uma ferramenta que requer o aprendizado de uma nova sintaxe ou padrão para uso. Closures são uma consequência natural da escrita de código baseado no escopo léxico. Raramente precisamos criar fechamentos intencionalmente ao escrever código.
Acredito que muitos amigos já estão murmurando em seus corações neste momento, o que é esse escopo lexical? Não entre em pânico, apenas me escute devagar. O escopo lexical é o escopo definido no estágio lexical. Em outras palavras, o escopo léxico é determinado por onde você coloca as variáveis e os escopos em nível de bloco ao escrever seu código, portanto, o escopo permanece inalterado quando o analisador léxico processa o código (na maioria das vezes). ——"JavaScript que você não conhece"
Vamos dar um exemplo primeiro:
function test(){ var arr = [] for(var i=0;i<10;i++){ arr[i]=função(){ console.log(i); } } retorno } var meuArr = teste() // meuArr[0]() // meuArr[1]() // ... for(var j = 0; j < 10; j++){ meuArr[j]() } //Para evitar tédio, um segundo loop é usado aqui para chamar a função no primeiro loop da função de teste e imprimir dez resultados.
Vamos analisar este código primeiro: Quando este código é executado, de acordo com o bom senso, ele. deve ser analisado Ele imprime dez números de 0 a 9 em sequência, mas o loop for não leva tempo para ser executado (negligenciado em microssegundos). Quando o teste da função retorna arr, há 10 function(){console log(i. );}, a função no array não é executada neste momento. Quando var myArr = test() chama a função de teste, como o tempo de execução do loop for é ignorado, i já é 10 neste momento, então qual é o valor? impresso é 10 em 10.
Acredito que alguém vai perguntar neste momento, o que isso tem a ver com o encerramento que vamos falar. Então, se modificarmos um pouco esse código e transformá-lo em um acumulador, como podemos implementá-lo?
Acredito que neste momento haverá figurões que dirão: não é tão simples?
Altere a definição var para uma definição let para que o primeiro loop for se torne um escopo em nível de bloco e, em seguida, possa se tornar um acumulador. Claro que não há problema,
mas o que estamos falando hoje é como implementar um acumulador no ES5. Então vamos dar uma olhada no seguinte código:
function test(){ var arr = [] for(var i=0;i<10;i++){ (função(j){ arr[j]=função(){ console.log(j); } })(eu) } retorno } var meuArr = teste() for(var j = 0; j < 10; j++){ meuArr[j]() }
Amigos cuidadosos certamente descobrirão que isso significa alterar o corpo da função no loop para uma função autoexecutável, mas o resultado da saída neste momento é gerar dez números de 0 a 9 em sequência, e isso inclui o pacote de fechamento, quando começarmos a executar este código, o segundo loop for será chamado dez vezes. Quando cada função autoexecutável for executada, um objeto AO da função autoexecutável será criado. O nome do atributo é j. Normalmente, após a execução da função de execução, seu objeto AO deve ser destruído. No entanto, quando myarr[j] () é executado, o objeto AO de arr[j] está no topo da cadeia de escopo. agora procurei o nome do atributo j, mas não o encontrei. Procurei na cadeia de escopo e o encontrei no objeto AO da função autoexecutável. Portanto, quando a função autoexecutável termina, seu AO. objeto não será reciclado pelo mecanismo de coleta de lixo, caso contrário, um erro será relatado quando myarr[j] () for executado e um fechamento será formado.
Vejamos outro exemplo
de função a(){. função b(){ var bbb = 234 console.log(aaa); } var aaa = 123 return b // b nasceu em a, mas foi salvo} varglob = 100 var demonstração = a()Primeiro usamos a pré-compilação para analisar o código de
demo()
. Primeiro, definimos um objeto GO global. Procure a declaração global e encontre a declaração da variável global. é indefinido. Encontre-o na declaração global. Para a declaração da função, o nome da função é usado como o nome do atributo do objeto GO e o valor é atribuído ao corpo da função. Neste momento deveria ser GO{ glob: undefined--->100; função a {} } e, finalmente, pré-compilar a função b na função a para criar um AO de b { b: indefinido ---> 234} neste momento, a ordem da cadeia de escopo é 1. Objeto AO da função b; 2. Objeto AO da função a; 3. Objeto GO global. Quando imprimimos aaa na função b, começamos do topo da cadeia de escopo. Se não houver aaa no objeto AO da função b, pesquisaremos ao longo da cadeia de escopo para encontrar o AO da função de segundo nível a. . O objetivo é encontrar o valor de aaa como 123 e gerar o resultado.
Se não o analisássemos da perspectiva da pré-compilação, pensaríamos que aaa deveria reportar um erro neste momento. Quando var demo = a() for executado, quando a execução da função a terminar, então o objeto AO correspondente a. a deve ser destruído. De acordo com a análise do senso comum: Quando executamos a demonstração, a cadeia de escopo deve criar o objeto AO e o objeto GO de b. Neste momento, existe apenas o objeto AO de b e nenhum objeto AO de a. O valor de aaa não deve ser impresso, mas neste momento, o valor de aaa O valor é 123, o que significa que o objeto AO de a não foi destruído, então por quê? A razão é que um fechamento é criado aqui. Quando a execução de var demo = a() for concluída, o mecanismo de coleta de lixo perguntará: Irmão, uma função, acho que você terminou de executá-la. Sua memória em execução pode ser liberada para mim. ?, mas neste momento a função a só conseguiu balançar a cabeça desamparadamente e disse: Irmão, não tenho certeza se concluí a execução. Depois de executá-la, criei um b, mas b não está sob meu controle, então estou. não tenho certeza se b. foi chamado, então não tenho certeza se concluí a execução. O mecanismo de coleta de lixo pensou nisso, não irei reciclá-lo. Devo relatar um erro, então neste momento o objeto AO não é reciclado.
Acredito que através desses dois exemplos você já tenha uma compreensão geral dos encerramentos. A seguir, vamos falar sobre as funções dos encerramentos.
A funçãode
fechamento é
- pode
- implementar variáveis públicas. Por exemplo: o acumulador (3.js)
- ser armazenado em cache
- para conseguir encapsulamento, privatização
- e desenvolvimento modular de atributos para evitar a contaminação de variáveis globais.
3.js).
var contagem = 0 função adicionar() { contagem de retorno++ } console.log(adicionar()); console.log(adicionar()); console.log(add());
Este é um código de acumulação relativamente comum, mas se durante nosso estágio ou mesmo no trabalho a empresa exigir que você encapsule o acumulador em um código modular, então neste momento, por uma questão de segurança. módulo Tentamos evitar ao máximo definir variáveis globais, mas como podemos conseguir isso sem definir variáveis globais. Neste momento podemos usar fechamentos;
função adicionar() { var contagem = 0 função a() { ++contagem console.log(contagem); } devolver um } var res = adicionar() res() res() //Após o término da função add, o objeto AO de add não é destruído, pois após a execução da função add, o a retornado forma um encerramento sem saber se foi chamado, para que possa ser encapsulado em um módulo sem usar variáveis globais.
Portanto, estas são algumas das minhas opiniões pessoais sobre os encerramentos e suas funções. No momento, tenho apenas uma compreensão superficial dos encerramentos. Após estudo e aprimoramento subsequentes, haverá artigos subsequentes sobre encerramentos. corrigir e progredir juntos.