Introdução
Um fechamento é uma função que tem permissão para acessar variáveis em outro escopo de função.
Os fechamentos são difíceis de entender em JavaScript. Muitos aplicativos avançados dependem de fechamentos para implementá -los. Vamos primeiro olhar para um exemplo abaixo:
função externo () {var i = 100; função interna () {console.log (i); }}No código acima, de acordo com o escopo da variável, todas as variáveis locais na função externa são visíveis para a função interna; As variáveis locais na função interna são invisíveis fora da função interna; portanto, as variáveis locais na função interna não podem ser lidas fora da função interna.
Como a função interna pode ler as variáveis locais da função externa, desde que o interno seja usado como o valor de retorno, as variáveis locais internas podem ser lidas diretamente fora do OUER.
função externo () {var i = 100; função interna () {console.log (i); } retornar interno;} var rs = outer (); rs ();Esta função tem duas características:
Depois de executar var rs = Outer () dessa maneira, o RS real aponta para a função interna. Este código é realmente um fechamento. Ou seja, quando a função interna dentro da função externa é referenciada por uma variável fora da função externa, um fechamento é criado.
Escopo
Simplificando, o escopo é a gama acessível de variáveis e funções, ou seja, o escopo controla a visibilidade e o ciclo de vida de variáveis e funções. Em JavaScript, o escopo das variáveis é global e local.
Escopo global
var num1 = 1; function fun1 () {num2 = 2;}Os três objetos acima Num1, Num2 e Fun1 são todos escopos globais. Deve -se notar aqui que as variáveis que definem atribuições diretas no final são declaradas automaticamente como tendo escopos globais;
Escopo local
function wrap () {var obj = "Estou embrulhado por embrulho, e o exterior do invólucro não pode me acessar diretamente"; função inerfun () {// O exterior não pode me acessar}}Cadeia de escopo
Tudo no JavaScript é um objeto. Esses objetos têm uma propriedade [[SCOPE]], que contém uma coleção de objetos no escopo criado pela função. Esta coleção é chamada de cadeia de escopo da função, que determina quais dados podem ser acessados pela função.
função add (a, b) {return a+b;}Quando uma função é criada, sua propriedade [[escopo]] adicionará automaticamente o escopo global
var sum = add (3,4);
Quando uma função é chamada, um objeto interno chamado contexto de execução é criado. Este objeto Z define o ambiente quando a função é executada. Ele também possui sua própria cadeia de escopo para resolução do identificador, e sua cadeia de escopo é inicializada como um objeto contido em [[escopo]] da função de corrida atual.
Durante a execução da função, toda vez que uma variável é encontrada, um processo de análise de identificador será passado para decidir onde obter e armazenar dados. Esse processo começa com a cabeça da cadeia de escopo, ou seja, procura um identificador com o mesmo nome do objeto ativo. Se for encontrado, use a variável correspondente a esse identificador. Se não for encontrado, continue pesquisando o próximo objeto na cadeia do escopo, se todos os objetos forem pesquisados (o último é um objeto global) não serão encontrados, o identificador será considerado indefinido.
Encerramento
Um fechamento é simplesmente uma função que acessa suas variáveis externas.
var quo = function (status) {return {getStatus: function () {retornar status; }}}O status é salvo no quo, ele retorna um objeto, o método getStatus nesse objeto refere -se à variável de status, ou seja, a função getStatus acessa seu status de variável externa;
var newValue = quo ('string'); // retorna um objeto anônimo, referenciado por newValue com newValue.getStatus (); // acessou o status variável interno do quoSe o método getStatus não estiver disponível, o status será automaticamente reciclado após o quo ('Sting'). É precisamente porque o objeto anônimo retornado é referenciado por um objeto global, e o objeto anônimo depende do status, portanto, impedirá a liberação de status.
exemplo:
// esquema de erro var test = function (nós) {var i; for (i = 0; i <modes.length; i ++) {nós [i] .OnClick = function (e) {alert (i); }}}Uma função anônima cria um fechamento, e o I Access Is i está na função de teste externo, então cada nó realmente se refere ao mesmo i.
// solução de melhoria var test = function (nós) {var i; for (i = 0; i <modes.length; i ++) {nós [i] .OnClick = function (i) {return function () {alert (i); }; }(eu); }}Cada nó está vinculado a um evento. Este evento recebe um parâmetro e funciona imediatamente, passando em i. Por ser passada por valor, cada loop gerará um novo backup para o i atual.
O papel do fechamento
função externo () {var i = 100; função interna () {console.log (i ++); } retornar interno;} var rs = outer (); rs (); // 100rs (); // 101rs (); // 102No código acima, RS é a função interna de fechamento. O RS foi executado três vezes no total, a primeira vez foi 100, a segunda vez foi 101 e a terceira vez foi 102. Isso mostra que a variável local I na função Exterior foi mantida na memória e não foi automaticamente limpa quando chamado.
O objetivo do fechamento é que, após a execução externa ser concluída e devolvida, o fechamento faz o mecanismo de coleta de lixo do JavaScript (coleção de captura) não reciclar a memória ocupada pela externa, porque a execução da função interna da parte externa depende das variáveis da saída. (Outra explicação: Exterior é a função pai do interior, o interno é atribuído a uma variável global, fazendo com que o interior fique na memória o tempo todo, e a existência de interno depende da externa, porque algum exterior está sempre na memória e não será coletado e reciclado após a chamada).
O fechamento tem permissão para acessar todas as variáveis dentro da função.
Quando uma função retorna um fechamento, o escopo da função será salvo na memória até que o fechamento não exista.
Fechamentos e variáveis
Devido ao mecanismo da cadeia de escopo, o fechamento pode obter apenas o último valor que contém qualquer variável na função. Veja o seguinte exemplo:
função f () {var rs = []; for (var i = 0; i <10; i ++) {rs [i] = function () {return i; }; } retornar rs;} var fn = f (); para (var i = 0; i <fn.length; i ++) {console.log ('function fn [' + i + '] () Valor de retorno:' + fn [i] ());}As funções retornarão uma matriz. Na superfície, parece que cada função deve retornar seu próprio valor de índice. De fato, cada função retorna 10. Isso ocorre porque a cadeia de escopo da primeira função contém os objetos ativos da função f e eles se referem à mesma variável i. Quando a função f retorna, o valor da variável I é 10. Nesse momento, cada função salva o mesmo objeto variável da variável i. Podemos forçar o fechamento a nos comportar conforme o esperado, criando outra função anônima.
função f () {var rs = []; for (var i = 0; i <10; i ++) {rs [i] = function (num) {return function () {return num; }; }(eu); } retornar rs;} var fn = f (); para (var i = 0; i <fn.length; i ++) {console.log ('function fn [' + i + '] () Valor de retorno:' + fn [i] ());}Nesta versão, em vez de atribuir o fechamento diretamente à matriz, definimos uma função anônima e atribuímos o resultado de executar imediatamente a função anônima à matriz. Aqui, a função anônima possui um parâmetro num. Ao chamar cada função, passamos na variável i. Como os parâmetros são passados por valor, a variável será copiada para o parâmetro num. Dentro desta função anônima, um NUM de acesso ao fechamento é criado e devolvido. Dessa forma, cada função na matriz RS possui uma cópia de sua própria variável num, para que valores diferentes possam ser retornados.
Este objeto para fechar
var name = 'jack'; var o = {name: 'bingdian', getName: function () {return function () {return this.name; }; }} console.log (o.getName () () () ()); // jackvar name = 'jack'; var o = {name: 'bingdian', getName: function () {var self = this; return function () {return self.name; }; }} console.log (o.getName () () () ()); // bingdianVazamento de memória
function cacktionHandler () {var el = document.getElementById ('Demo'); el.OnClick = function () {console.log (el.id); }} cessionHandler ();O código acima cria um fechamento como o manipulador de eventos do elemento EL, e esse fechamento cria uma referência circular. Enquanto a função anônima existir, o número de referências de EL for pelo menos 1, porque a memória que ocupa nunca será reciclada.
function cacktionHandler () {var el = document.getElementById ('Demo'); var id = el.id; el.onClick = function () {console.log (id); } el = null;} atributHandler ();Definir a variável El Null pode desreferenciar o objeto DOM e garantir que ele consome memória normalmente.
Imite o escopo no nível do bloco
A instrução definida em qualquer par de aparelhos encaracolados ({e}) pertence a um bloco, e todas as variáveis definidas são invisíveis fora do bloco de código, que chamamos de escopo no nível do bloco.
(function () {// Block-Level Scope}) ();Aplicação de fechamentos
Proteja a segurança das variáveis na função. Como no exemplo anterior, apenas a função interna pode acessar i na função externa, mas não pode ser acessada através de outros canais, protegendo assim a segurança de i.
Manter uma variável na memória. Como no exemplo anterior, devido ao fechamento, o i na função externo sempre existe na memória; portanto, sempre que Rs () for executado, serei adicionado 1.