Reabastecer:
O fechamento é uma dificuldade na linguagem JavaScript e em seu recurso. Muitos aplicativos avançados dependem de fechamentos para implementar.
Recursos de fechamento
Os fechamentos têm três características:
1. Funções aninhadas de função
2. A função pode se referir a parâmetros e variáveis externos dentro
3. Parâmetros e variáveis não serão coletados pelo mecanismo de coleta de lixo
Definição de fechamento e suas vantagens e desvantagens
Os fechamentos se referem a funções que têm acesso a variáveis no escopo de outra função. A maneira mais comum de criar fechamentos é criar outra função dentro de uma função e acessar variáveis locais dessa função por meio de outra função.
A desvantagem dos fechamentos é que eles são memória residente, o que aumentará o uso da memória, e o uso inadequado pode levar facilmente ao vazamento de memória.
Os fechamentos são uma característica importante da linguagem JavaScript. A principal aplicação de fechamentos é principalmente para: projetar métodos e variáveis privados.
Depois que a função geral é executada, o objeto ativo local é destruído e apenas o escopo global é salvo na memória. Mas a situação de fechamento é diferente!
Para falar sobre o tópico
1. Definição de fechamento?
Vamos dar uma olhada em algumas definições sobre fechamentos:
1. O fechamento refere -se a uma função que tem permissão para acessar variáveis em outro escopo de função.
2. Os objetos de função podem ser associados através de cadeias de escopo e as variáveis dentro do corpo da função podem ser salvas no escopo da função. Essa característica é chamada de 'fechamento'.
3. As funções internas podem acessar parâmetros e variáveis de funções externas que as definem (exceto isso e argumentos).
Se você deseja aprender sistematicamente o conceito de fechamento do JS, pode consultar o site da JS E-Book do Wulin.com para aprender.
Vamos resumir a definição
1. Você pode acessar funções de variáveis no escopo da função externa
2. As variáveis de funções externas acessadas por funções internas podem ser salvas no escopo da função externa sem serem recicladas-esse é o núcleo. Mais tarde, quando encontramos fechamentos, devemos pensar nisso. Devemos nos concentrar na variável referenciada pelo fechamento.
Crie um fechamento simples
var sayName = function () {var name = 'jozo'; return function () {alert (name);}}; var s à SayName (); dizer();Vamos interpretar as próximas duas frases:
• var diz = SayName (): retorna uma função interna anônima armazenada na variável Say e refere -se ao nome da variável da função externa. Devido ao mecanismo de coleta de lixo, após a execução da função SayName, o nome da variável não é destruído.
• Diga (): Execute a função interna retornada e ainda acesse o nome da variável e a saída 'Jozo'.
2.
Compreender as cadeias de escopo também é útil para entender o fechamento.
Os métodos de pesquisa de variáveis no escopo devem ser muito familiares, mas, na verdade, é isso que está pesquisando para cima ao longo da cadeia de escopo.
Quando a função é chamada:
1. Primeiro, crie um contexto de execução e a cadeia de escopo correspondente;
2. Adicione argumentos e outros valores de parâmetros nomeados ao objeto ativo da função (objeto de ativação)
Cadeia de escopo: o objeto ativo da função atual tem a maior prioridade, seguida pelo objeto ativo da função externa, e o objeto ativo da função externa da função externa diminui na sequência até o final da cadeia do escopo - o escopo global. Prioridade é a ordem das pesquisas variáveis;
Vamos primeiro olhar para uma corrente de escopo normal:
function SayName (name) {retorna nome;} var says = SayName ('jozo');Este código contém dois escopos: a. escopo global; B.SayName Function Scope, ou seja, existem apenas dois objetos variáveis. Quando executado no ambiente de execução correspondente, o objeto variável se tornará um objeto ativo e será empurrado para a extremidade frontal da cadeia de escopo do ambiente de execução, ou seja, se torna a maior prioridade. Fale com a foto:
Esta imagem também está disponível nos livros de programação JS Advanced, e eu re-desenhei tudo.
Ao criar a função SayName (), uma cadeia de escopo que contém o objeto variável é criada com antecedência, ou seja, a cadeia de escopo indexada por 1 na figura e é salva no atributo [[escopo]] interno. Quando a função SayName () é chamada, um ambiente de execução é criado e, em seguida, a cadeia de escopo é construída copiando o objeto no atributo [[escopo]] da função. Depois disso, outro objeto ativo (indexado por 0 na figura) é criado e empurrado para a extremidade frontal da cadeia de escopo do ambiente de execução.
De um modo geral, quando a função é executada, o objeto ativo local será destruído e apenas o escopo global é salvo na memória. No entanto, a situação dos fechamentos é diferente:
Vamos dar uma olhada na cadeia de fechamentos de escopo:
function SayName (name) {return function () {return name;}} var diz = SayName ('jozo');Esta instância de fechamento tem mais um escopo para a função anônima do que o exemplo anterior:
Depois que a função anônima é retornada da função SayName (), sua cadeia de escopo é inicializada para um objeto ativo e um objeto variável global que contém a função SayName (). Dessa maneira, a função anônima pode acessar todas as variáveis e parâmetros definidos em SayName (). Mais importante, após a execução da função SayName (), seu objeto ativo não será destruído, porque a cadeia de escopo da função anônima ainda se refere ao objeto ativo. Em outras palavras, após a execução da função SayName (), a cadeia de escopo de seu ambiente de execução será destruída, mas seu objeto ativo será deixado na memória, sabendo que a função anônima será destruída. Este também é o problema de vazamento de memória que será discutido mais adiante.
Não escrevo muito sobre questões de cadeia de escopo e escrever coisas no livro também é muito cansativo o (□) o
3. Exemplo de fechamento
Exemplo 1: Implementação de acumulação
// Método 1Var a = 0; var add = function () {a ++; console.log (a)} add (); add (); // método 2: fechamento var add = (function () {var a = 0; retorna função () {a ++; console.log (a);}}) (); console.og.og (a); a); // undefinedAdd (); add ();Em comparação, o método 2 é mais elegante e também reduz as variáveis globais e privatiza variáveis.
Exemplo 2: Adicione o evento de clique em cada li
var oli = document.getElementsByTagName ('li'); var i; para (i = 0; i <5; i ++) {oli [i] .OnClick = function () {alert (i);}} console.log (i); // 5 // Execute a função anônima (function () {alert (i); // 5} ());O acima é um exemplo clássico. Todos sabemos que o resultado da execução é que 5 aparecem, e também sabemos que os fechamentos podem ser usados para resolver esse problema, mas no começo ainda não consigo entender por que 5 aparecem e por que os fechamentos podem resolver esse problema. Mais tarde, resolvi e deixei claro:
um. Vamos primeiro analisar a situação antes que o fechamento seja usado: no loop for, ligamos uma função anônima a cada evento de cliques de LI e o valor da variável I retorna na função anônima. Quando o loop termina, o valor da variável I se torna 5. Nesse momento, clicamos em cada LI, ou seja, executam a função anônima correspondente (consulte o código acima). Essa é a variável I já é 5, então cada clique aparece 5. Como cada função anônima retornada aqui refere -se à mesma variável i, se criarmos uma nova variável para salvar o valor atual de i quando o loop é executado, deixamos que a função anônima aplique essa variável e, finalmente, retorne esta função anônima, de modo que nosso objetivo pode ser alcançado. Isso é alcançado usando fechamentos!
b. Vamos analisar a situação ao usar o fechamento:
var oli = document.getElementsByTagName ('li'); var i; for (i = 0; i <5; i ++) {oli [i] .OnClick = (function (num) {var a = num; // para ilustrar o problema de retorno da função () {ALERT (a);}}) (i)} console.og (i); // 5Quando o loop for executado, a função anônima ligada ao evento de clique é passada i e executada imediatamente para retornar uma função anônima interna. Como os parâmetros são passados pelo valor, o número de parâmetro formal salva o valor atual de I e, em seguida, atribui o valor à variável local a. Em seguida, a função anônima interna mantém a referência de A, ou seja, mantém o valor atual de i. Portanto, após a execução do loop, clique em cada LI e a função anônima retornada exibirá o valor do salvo a.
4. Aplicação de fechamentos
Vamos dar uma olhada no objetivo do fechamento. De fato, usando o fechamento, podemos fazer muitas coisas. Por exemplo, simular o estilo de código orientado a objetos; Expresse código de maneira mais elegante e concisa; e melhorar a eficiência da execução do código em alguns aspectos.
1. Função auto-executiva anônima
Em situações reais, geralmente encontramos uma situação em que algumas funções precisam ser executadas uma vez e suas variáveis internas não precisam ser mantidas, como a inicialização da interface do usuário, para que possamos usar o fechamento:
// altere todas as fontes li para vermelho (function () {var els = document.getElementsByTagName ('li'); para (var i = 0, lng = els.length; i <lng; i ++) {els [i] .style.color = 'Red';}}) ();Criamos uma função anônima e a executamos imediatamente. Como o externo não pode se referir às variáveis dentro dele, variáveis locais como ELs, eu e o GNL serão lançadas logo após a execução, salvando memória!
A chave é que esse mecanismo não poluirá o objeto global.
2. Implementar o encapsulamento/código modular
var pessoa = function () {// O escopo da variável está dentro da função, e o var name = "padrão" não pode ser acessado fora. return {getName: function () {return name; }, setName: function (newName) {name = newName; }}} (); console.log (Person.name); // Acesso direto, o resultado é indefinido console.log (Person.getName ()); // Pessoa padrão.SetName ("Jozo"); console.log (Person.getName ()); // jozo3. Implemente orientado a objetos
Dessa maneira, diferentes objetos (instâncias de classes) têm membros e estados independentes e não interferem entre si. Embora não exista um mecanismo como classe em JavaScript, usando fechamentos, podemos simular esses mecanismos. Vamos falar sobre o exemplo acima:
function Person () {var name = "padrão"; return {getName: function () {return name; }, setName: function (newName) {name = newName; }}}; var pessoa1 = pessoa (); print (Person1.getName ()); John.setName ("Person1"); print (Person1.getName ()); // pessoa1 var pessoa2 = pessoa (); print (Person2.getName ()); jack.setName ("ERSOL2"); print (erson2.getName ()); // Person2Duas instâncias da pessoa Pessoa 1 e Person2 não interferem entre si! Porque essas duas instâncias têm acesso independente ao membro do nome.
5. Vazamentos e soluções de memória
Mecanismo de reciclagem de lixo
Falando em gerenciamento de memória, é naturalmente inseparável do mecanismo de coleta de lixo em JS. Existem duas estratégias para realizar a coleta de lixo: autorização de marcas e contagem de referência ;
Remoção de marcas: Quando o coletor de lixo for executado, marcará todas as variáveis armazenadas na memória. Em seguida, ele removerá as tags de variáveis no ambiente e as tags de variáveis referenciadas por variáveis no ambiente. Depois disso, se a variável estiver marcada novamente, significa que a variável está pronta para ser excluída. Até 2008, ou seja, JavaScript do Firefox, Opera, Chrome e Safari usaram esse método;
Contagem de referência: rastrear o número de vezes que cada valor é referenciado. Quando uma variável é declarada e um valor de um tipo de referência é atribuído à variável, o número de vezes que esse valor é referenciado é 1. Se esse valor for atribuído a outra variável, o número de vezes que uma referência é aumentada em 1. Pelo contrário, se uma variável se desviará da referência do número, o número de referências é reduzido por 1 e quando uma vez que a número é 0,00, o número de referências é reduzido por 1 e quando uma variável é 0,00, a referência é que a referência é reduzida.
Um grande problema com esse método é a referência circular, ou seja, o objeto A contém um ponteiro para B, e o objeto B também contém uma referência a A. Isso pode fazer com que uma grande quantidade de memória seja reciclada (vazamentos de memória), porque suas referências nunca podem ser 0. Nas versões IE (IE4-IE6) adotaram um mecanismo de coleta de lixo que foi contado. Uma das razões pelas quais o fechamento causou vazamento de memória foi uma falha desse algoritmo.
Sabemos que alguns objetos no IE não são objetos nativos de JavaScript. Por exemplo, objetos em Bom e DOM são implementados na forma de objetos COM, e o mecanismo de coleta de lixo dos objetos COM adota a contagem de referência. Portanto, embora o JavaScript Engine do IE adote uma estratégia de limpeza de tags, o acesso a objetos COM ainda é baseado na contagem de referência; portanto, desde que os objetos COM sejam projetados no IE, haverá um problema de referências circulares!
Pegue uma castanha:
window.onload = function () {var el = document.getElementById ("id"); el.onclick = function () {alert (el.id);}}Por que esse código causa vazamentos de memória?
el.OnClick = function () {alert (el.id);};Ao executar esse código, o objeto de função anônimo é atribuído ao atributo onclick do EL; Então a função anônima refere -se ao objeto El dentro, e há uma referência circular, portanto não pode ser reciclada;
Solução:
window.onload = function () {var el = document.getElementById ("id"); var id = el.id; // não referência el.onclick = function () {alert (id); } el = null; // limpe o objeto ativo na função externa referenciada pelo fechamento}O exposto acima é o resumo do conhecimento relevante sobre o vazamento de memória de coleta de lixo da corrente de fechamento JS introduzido pelo editor. Espero que seja útil para todos!