Explicação detalhada do fechamento js

Fecho

Perguntas reais clássicas

  • O que é um fechamento? Quais são os cenários de aplicação de fechamentos? Como destruir o fechamento?

o que é fechamento

O fechamento é um ponto de conhecimento muito importante em JavaScript e também um dos pontos de conhecimento que provavelmente será questionado em nossas entrevistas front-end.

Abra " Programação Avançada de JavaScript " e " Guia Definitivo de JavaScript " e você descobrirá que existem diferentes explicações para fechamentos. Pesquisando conteúdo sobre fechamentos na Internet também descobre que existem opiniões diferentes, o que leva ao fato de que este ponto de conhecimento em si parece um pouco misterioso, até um pouco fantasioso.

Então esse ponto de conhecimento é realmente tão profundo?

Não! Na verdade, é muito fácil entender os encerramentos em JavaScript , mas antes disso você precisa conhecer os dois pontos de conhecimento a seguir:

  • Escopo e cadeias de escopo em JavaScript
  • Coleta de lixo em JavaScript

Aqui, revisamos brevemente esses dois pontos de conhecimento:

1. Escopo e cadeia de escopo em JavaScript

  • O escopo é um território independente, para que as variáveis ​​não sejam vazadas ou expostas e as variáveis ​​com o mesmo nome em escopos diferentes não entrem em conflito.
  • O escopo é determinado quando é definido e não muda.
  • Se nenhum valor for encontrado no escopo atual, ele irá pesquisar no escopo superior até que o escopo global seja encontrado.A cadeia formada por tal processo de pesquisa é chamada de cadeia de escopo.

2. Coleta de lixo em JavaScript

  • O ambiente de execução Javascript é responsável por gerenciar a memória utilizada durante a execução do código, o que envolve um mecanismo de coleta de lixo.
  • O coletor de lixo irá periodicamente (periodicamente) descobrir aquelas variáveis ​​que não são mais utilizadas.Enquanto a variável não for mais utilizada, ela será recuperada pelo coletor de lixo e sua memória será liberada. Se a variável ainda estiver em uso, ela não será reciclada.

OK , com esses dois pontos de conhecimento em mente, vamos ver o que é encerramento.

O fechamento não é uma técnica específica, mas um fenômeno que significa que quando uma função é definida, informações do ambiente circundante podem ser utilizadas na função. Ou seja, ao executar uma função, sempre que dados externos são utilizados na função, é criado um encerramento.

A cadeia de escopo é o meio para realizar o fechamento.

O que? Enquanto dados externos são usados ​​em uma função, um encerramento é criado?

Realmente? Aqui podemos provar isso:

imagem-20211227145016552

No código acima, definimos uma variável i na função a e , em seguida, imprimimos a variável i . Para a função a , a variável i existe em seu próprio escopo de função , então podemos ver que a variável i existe no Local durante a depuração .

Vamos modificar ligeiramente o código acima, conforme mostrado abaixo:

imagem-20211227145521272

No código acima, colocamos a ação de declarar a variável i fora da função a . Isso significa que a função a não pode mais encontrar a variável i em seu próprio escopo . O que ela fará?

Se você estudou a cadeia de escopo, deve saber que ela será observada camada por camada ao longo da cadeia de escopo. Porém, conforme mencionado acima na introdução de fechamentos, caso isso aconteça, ou seja, quando a função utilizar dados externos, será criado um fechamento.

Observando atentamente a área de depuração, descobriremos que i está colocado em Closure neste momento , confirmando assim nossa afirmação anterior.

Então você vê, os encerramentos não são tão difíceis de entender. Quando você sentir que uma palavra é particularmente difícil para você, você também pode usar o método de divisão de palavras. Este também é um método experimentado e testado que eu recomendo.

"Fechado" pode ser entendido como "circuito fechado" e "pacote" pode ser entendido como "um espaço semelhante a um pacote". Portanto, o fechamento pode realmente ser considerado um espaço fechado. Então, para que serve esse espaço? ? Na verdade, é usado para armazenar variáveis.

imagem-20211227163947135

Então, todas as declarações de variáveis ​​sob uma função serão colocadas no espaço fechado do fechamento?

Na verdade não, colocá-la no fechamento depende se a variável é referenciada em outro lugar, por exemplo:

imagem-20211227164333723

No código acima, nenhuma variável é criada na função c, mas i, j, k e x são impressas . Essas variáveis ​​existem nas funções a, b e no escopo global, respectivamente. Portanto, três fechamentos são criados e o global O valor de i é armazenado no encerramento , os valores das variáveis ​​j e k são armazenados no encerramento a e o valor da variável x é armazenado no encerramento b .

Mas se você olhar com atenção, descobrirá que a variável y na função b não está colocada no fechamento, portanto, colocá-la no fechamento depende se a variável é referenciada.

Claro, você pode ter um novo problema neste momento: com tantos fechamentos, eles não ocupam espaço na memória?

Na verdade, se o fechamento for formado automaticamente, ele será destruído. Por exemplo:

imagem-20211227174043786

No código acima, tentamos imprimir a variável k na linha 16. Obviamente, um erro será relatado neste momento. Ao definir um ponto de interrupção na linha 16 para depuração, podemos ver claramente que não há fechamento neste momento O coletor de lixo reciclará automaticamente variáveis ​​não referenciadas sem qualquer uso de memória.

Claro, estou me referindo aqui à geração automática de fechamentos. Em relação aos fechamentos, às vezes precisamos criar manualmente um fechamento com base nos requisitos.

Considere o seguinte exemplo:

function eat(){
    
    
    var food = "鸡翅";
    console.log(food);
}
eat(); // 鸡翅
console.log(food); // 报错

No exemplo acima, declaramos uma função chamada eat e a chamamos.

O mecanismo JavaScript criará um contexto de execução para a função eat , no qual a variável food é declarada e recebe um valor.

Quando este método é executado, o contexto é destruído e a variável food desaparece. Isso ocorre porque a variável food é uma variável local da função eat , ela atua na função eat e será criada e destruída conforme o contexto de execução de eat for criado. Portanto, quando imprimirmos a variável food novamente, um erro será relatado, informando que a variável não existe.

Mas vamos modificar um pouco este código:

function eat(){
    
    
    var food = '鸡翅';
    return function(){
    
    
        console.log(food);
    }
}
var look = eat();
look(); // 鸡翅
look(); // 鸡翅

Neste exemplo, a função eat retorna uma função e a variável local food é acessada nesta função interna . Chame a função eat e atribua o resultado à variável look.Essa look aponta para a função interna na função eat , depois a chama e, finalmente, gera o valor de food .

A razão pela qual os alimentos podem ser acessados ​​​​é muito simples: como dissemos acima, o coletor de lixo reciclará apenas variáveis ​​​​que não são referenciadas, mas uma vez que uma variável ainda seja referenciada, o coletor de lixo não reciclará essa variável. No exemplo acima, food deveria ser destruído após chamar eat , mas retornamos a função anônima dentro de eat para fora , e essa função anônima faz referência a food , então o coletor de lixo não irá reciclá-la. Sim, é por isso que quando esta função anônima é chamado de fora, o valor da variável food ainda pode ser impresso.

Neste ponto, foi revelada uma das vantagens ou características dos fechamentos, a saber:

  • Os fechamentos permitem que o ambiente externo acesse variáveis ​​locais dentro de uma função.
  • Os fechamentos permitem que variáveis ​​locais sejam persistidas e não destruídas junto com seu contexto.

Através deste recurso, podemos resolver o problema da poluição variável global. Nos primeiros dias , quando o JavaScript não podia ser modularizado, quando várias pessoas colaboravam, se muitas variáveis ​​globais fossem definidas, isso poderia causar conflitos de nomenclatura de variáveis ​​globais. Os fechamentos eram usados ​​para resolver as chamadas de função para variáveis ​​e escrever variáveis ​​em um espaço independente. No interior, pode resolver até certo ponto o problema da poluição variável global.

Por exemplo:

var name = "GlobalName";
// 全局变量
var init = (function () {
    
    
    var name = "initName";
    function callName() {
    
    
        console.log(name);
        // 打印 name
    }
    return function () {
    
    
        callName();
        // 形成接口
    }
}());
init(); // initName
var initSuper = (function () {
    
    
    var name = "initSuperName";
    function callName() {
    
    
        console.log(name);
        // 打印 name
    }
    return function () {
    
    
        callName();
        // 形成接口
    }
}());
initSuper(); // initSuperName

Ok, no final desta seção, vamos fazer um pequeno resumo dos fechamentos:

  • Um encerramento é um espaço fechado que armazena o valor do escopo que será referenciado em outro lugar. Em JavaScript , é um encerramento implementado através de uma cadeia de escopo.

  • Desde que sejam utilizados dados externos na função,é criado um encerramento.Neste caso,não precisamos de nos preocupar com o encerramento criado neste caso durante a codificação.

  • Também podemos criar fechamentos manualmente através de alguns meios, para que o ambiente externo possa acessar as variáveis ​​locais dentro da função, para que as variáveis ​​locais possam continuar a ser salvas e não destruídas junto com seu contexto.

Problema clássico de fechamento

Depois de falar sobre fechamentos, vejamos um problema clássico de fechamentos.

for (var i = 1; i <= 3; i++) {
    
    
    setTimeout(function () {
    
    
        console.log(i);
    }, 1000);
}

No código acima, nosso resultado esperado é gerar os valores da variável i como 1, 2 e 3, respectivamente, após 1 segundo . Porém, o resultado da execução é: 4, 4, 4 .

Na verdade, o problema está nos fechamentos. Veja bem, setTimeout no loop acessa sua variável externa i , formando um fechamento.

Há apenas uma variável i , então a mesma variável é acessada em setTimeout que faz um loop três vezes . Quando o loop atinge a 4ª vez , a variável i aumenta para 4 , a condição do loop não é atendida, o loop termina e o contexto termina após a execução do código. Porém, os três setTimeouts aguardam 1 segundo antes de serem executados. Devido ao fechamento, eles ainda podem acessar a variável i , mas o valor da variável i já é 4 neste momento .

Para resolver este problema, podemos deixar a função anônima em setTimeout não acessar mais variáveis ​​externas, mas acessar suas próprias variáveis ​​internas, como segue:

for (var i = 1; i <= 3; i++) {
    
    
    (function (index) {
    
    
        setTimeout(function () {
    
    
            console.log(index);
        }, 1000);
    })(i)
}

Desta forma, não há necessidade de acessar a variável i declarada no loop for em setTimeout . Em vez disso, passo o valor da variável i para setTimeout chamando uma função para passar parâmetros , para que eles não criem mais um fechamento, pois a variável i pode ser encontrada em meu próprio escopo.

Claro, existe uma maneira mais fácil de resolver esse problema, que é usar a palavra-chave let no ES6 .

A variável que ele declara tem escopo de bloco. Se você colocar em um loop, então haverá uma nova variável i toda vez que ele fizer um loop , então mesmo que haja um encerramento, não haverá problema, pois cada encerramento salva um i diferente variável, então o problema agora será resolvido.

for (let i = 1; i <= 3; i++) {
    
    
    setTimeout(function () {
    
    
        console.log(i);
    }, 1000);
}

resposta de pergunta real

  • O que é um fechamento? Quais são os cenários de aplicação de fechamentos? Como destruir o fechamento?

Um encerramento é um espaço fechado que armazena o valor do escopo que será referenciado em outro lugar. Em JavaScript , é um encerramento implementado através de uma cadeia de escopo.

Desde que sejam utilizados dados externos na função,é criado um encerramento.Neste caso,não precisamos de nos preocupar com o encerramento criado neste caso durante a codificação.

Também podemos criar fechamentos manualmente através de alguns meios, para que o ambiente externo possa acessar as variáveis ​​locais dentro da função, para que as variáveis ​​locais possam continuar a ser salvas e não destruídas junto com seu contexto.

O uso de fechamentos pode resolver o problema da poluição variável global.

Se for um fechamento gerado automaticamente, não precisamos nos preocupar com a destruição do fechamento. Se for um fechamento criado manualmente, podemos definir a variável referenciada como nula, ou seja, limpar manualmente a variável, para que o próximo momento em que o coletor de lixo JavaScript realiza a coleta de lixo Durante a reciclagem, caso seja constatado que esta variável não possui mais referência, o valor definido como nulo será reciclado.


-EOF- _ _

Acho que você gosta

Origin blog.csdn.net/qq_53461589/article/details/132740029
Recomendado
Clasificación