Como funcionam os encerramentos de JavaScript?

como explicaria os encerramentos de JavaScript a alguém com um conhecimento dos conceitos em que consistem (por exemplo, funções, variáveis e afins), mas não compreende os encerramentos em si?

Eu vi o exemplo do esquema {[[5]} dado na Wikipédia, mas infelizmente não ajudou.

Author: Zaheer Ahmed, 2008-09-21

30 answers

Fechos JavaScript para iniciantes

Apresentado por Morris na Tue, 2006-02-21 10:19. Editado pela comunidade desde então.
Os encerramentos não são mágicos.

Esta página explica encerramentos para que um programador possa compreendê-los-usando o código de JavaScript funcional. Não é para gurus ou programadores funcionais.

Os encerramentos não são difíceis de entender, uma vez que o conceito central está grokked. No entanto, são impossíveis de entender lendo qualquer teoria ou explicações academicamente orientadas!

Este artigo destina-se a programadores com alguma experiência de programação numa linguagem corrente, e que podem ler a seguinte função JavaScript:

function sayHello(name) {
  var text = 'Hello ' + name;
  var say = function() { console.log(text); }
  say();
}
sayHello('Joe');
Dois resumos breves
  • Quando uma função (foo) declara outras funções (bar e baz), a família de variáveis locais criadas em foo não é destruída quando a função sai. As variáveis apenas tornar-se invisível para o mundo exterior. Foo pode, portanto, ardilosamente retornar as funções bar baz, e eles podem continuar a ler, escrever e comunicar-se uns com os outros através deste fechado, fora da família de variáveis ("fechamento") que ninguém pode se intrometer, nem mesmo alguém que chama foo novamente no futuro.

  • Um fechamento é uma forma de suportar funções de primeira classe; é uma expressão que pode referenciar variáveis dentro do seu âmbito (quando foi a primeira vez que foi declarado), ser atribuído a uma variável, ser passado como argumento para uma função, ou ser retornado como resultado de uma função.

Um exemplo de encerramento

O seguinte código devolve uma referência a uma função:

function sayHello2(name) {
  var text = 'Hello ' + name; // Local variable
  var say = function() { console.log(text); }
  return say;
}
var say2 = sayHello2('Bob');
say2(); // logs "Hello Bob"

A maioria dos programadores JavaScript compreenderá como uma referência a uma função é devolvida a uma variável (say2) no código acima. Se não o fizeres, tens de ver isso antes de aprenderes. encerramento. Um programador usando C pensaria na função como retornando um ponteiro para uma função, e que as variáveis say e say2 eram cada um um ponteiro para uma função.

Existe uma diferença crítica entre um ponteiro C para uma função e uma referência JavaScript para uma função. Em JavaScript, você pode pensar em uma variável de referência de função como tendo tanto um ponteiro para uma função como como um ponteiro escondido para um fechamento.

O código acima tem um encerramento porque a função anônima function() { console.log(text); } é declarada dentro de outra função, sayHello2() Neste exemplo. Em JavaScript, se você usar a palavra-chave function dentro de outra função, você está criando um fechamento.

Em C e na maioria das outras línguas comuns, depois de uma função retorna, todas as variáveis locais não são mais acessíveis porque a pilha-frame é destruída.

Em JavaScript, se você declarar uma função dentro de outra função, então as variáveis locais do exterior a função pode permanecer acessível após retornar dela. Isto é demonstrado acima, porque chamamos a função say2() Depois de termos regressado de sayHello2(). Note que o código a que chamamos referencia a variável text, que era uma variável local da funçãosayHello2().

function() { console.log(text); } // Output of say2.toString();

Olhando para a saída de say2.toString(), podemos ver que o código se refere à variável text. A função anônima pode referenciar text que detém o valor 'Hello Bob' porque as variáveis locais de Foram secretamente mantidos vivos num encerramento.

O génio é que em JavaScript uma referência de função também tem uma referência secreta ao fecho em que foi criado - semelhante a como os delegados são um ponteiro de método mais uma referência secreta a um objecto.

Mais exemplos

Por alguma razão, os encerramentos parecem muito difíceis de entender quando se lê sobre eles, mas quando se vêem alguns exemplos, torna-se claro como funcionam (demorei algum tempo). Eu recomendo trabalhar através dos exemplos cuidadosamente até que você entenda como eles funcionam. Se você começar a usar fechamentos sem entender completamente como eles funcionam, você logo criaria alguns bugs muito estranhos!

Exemplo 3

Este exemplo mostra que as variáveis locais não são copiadas - são mantidas por referência. É como se a estrutura da pilha permanecesse viva na memória mesmo depois da função externa existir!

function say667() {
  // Local variable that ends up within closure
  var num = 42;
  var say = function() { console.log(num); }
  num++;
  return say;
}
var sayNumber = say667();
sayNumber(); // logs 43

Exemplo 4

Todos três funções globais têm uma referência comum aomesmo porque são todas declaradas dentro de uma única chamada para setupSomeGlobals().

var gLogNumber, gIncreaseNumber, gSetNumber;
function setupSomeGlobals() {
  // Local variable that ends up within closure
  var num = 42;
  // Store some references to functions as global variables
  gLogNumber = function() { console.log(num); }
  gIncreaseNumber = function() { num++; }
  gSetNumber = function(x) { num = x; }
}

setupSomeGlobals();
gIncreaseNumber();
gLogNumber(); // 43
gSetNumber(5);
gLogNumber(); // 5

var oldLog = gLogNumber;

setupSomeGlobals();
gLogNumber(); // 42

oldLog() // 5

As três funções partilharam o acesso ao mesmo Encerramento-as variáveis locais de setupSomeGlobals() quando as três funções foram definidas.

Note que no exemplo acima, se você chamar setupSomeGlobals() novamente, então um novo fechamento (pilha-frame!) é criada. O velho gLogNumber, gIncreaseNumber, gSetNumber variáveis são substituídos por novas funções que têm o novo encerramento. (Em JavaScript, sempre que você declara uma função dentro de outra função, A(S) função (s) interna (S) é/são recriadas novamente cada tempo em que a função externa é chamada.)

Exemplo 5

Este exemplo mostra que o fecho contém quaisquer variáveis locais que foram declaradas dentro da função externa antes de sair. Note que a variável alice é realmente declarada após a função anônima. O a função anônima é declarada primeiro, e quando essa função é chamada ELA Pode acessar a variável alice porque alice está no mesmo escopo (JavaScript faz a variável hoisting ). Além disso sayAlice()() apenas chama diretamente a referência da função retornada de sayAlice() - é exatamente o mesmo que foi feito anteriormente, mas sem a variável temporária.

function sayAlice() {
    var say = function() { console.log(alice); }
    // Local variable that ends up within closure
    var alice = 'Hello Alice';
    return say;
}
sayAlice()();// logs "Hello Alice"

Tricky: note também que a variável say também está dentro do fecho, e pode ser acessado por qualquer outra função que possa ser declarada dentro de sayAlice(), ou pode ser acessada recursivamente dentro da função interna.

Exemplo 6

Este é um verdadeiro "Apanhei-te" para muitas pessoas, por isso tens de o compreender. Tenha muito cuidado se você está definindo uma função dentro de um laço: as variáveis locais do fechamento podem não agir como você pode pensar primeiro.

Você precisa entender o recurso" variable hoisting " em Javascript, a fim de entender exemplo.

function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) {
        var item = 'item' + i;
        result.push( function() {console.log(item + ' ' + list[i])} );
    }
    return result;
}

function testList() {
    var fnlist = buildList([1,2,3]);
    // Using j only to help prevent confusion -- could use i.
    for (var j = 0; j < fnlist.length; j++) {
        fnlist[j]();
    }
}

 testList() //logs "item2 undefined" 3 times

A linha result.push( function() {console.log(item + ' ' + list[i])} adiciona uma referência a uma função anónima três vezes ao conjunto de resultados. Se você não está tão familiarizado com funções anônimas pense nisso como:

pointer = function() {console.log(item + ' ' + list[i])};
result.push(pointer);

Note que quando você executa o exemplo, {[40] } é registrado três vezes! Isto porque, tal como os exemplos anteriores, existe apenas um fecho para as variáveis locais para buildList (que são result, i e item). Quando o anónimo funções são chamadas na linha fnlist[j](); todos eles usam o mesmo do encerramento, e eles usam o valor atual para i e item dentro de um fechamento (onde i tem um valor de 3 uma vez que o ciclo tinha concluído, e item tem um valor de 'item2'). Note que estamos indexando a partir de 0 portanto item tem um valor de item2. E o i++ irá aumentar i para o valor 3.

Pode ser útil ver o que acontece quando é utilizada uma declaração ao nível do bloco da variável item (via a palavra-chave let) em vez de uma declaração variável de âmbito funcional via a palavra-chave var. Se essa mudança for feita, então cada função anônima no array result tem seu próprio fechamento; quando o exemplo é executado, o resultado é o seguinte:

item0 undefined
item1 undefined
item2 undefined

Se a variável i também estiver definida usando let em vez de var, então a saída é:

item0 1
item1 2
item2 3

Exemplo 7

Neste exemplo final, cada chamada para a função principal cria um separado encerramento.

function newClosure(someNum, someRef) {
    // Local variables that end up within closure
    var num = someNum;
    var anArray = [1,2,3];
    var ref = someRef;
    return function(x) {
        num += x;
        anArray.push(num);
        console.log('num: ' + num +
            '; anArray: ' + anArray.toString() +
            '; ref.someVar: ' + ref.someVar + ';');
      }
}
obj = {someVar: 4};
fn1 = newClosure(4, obj);
fn2 = newClosure(5, obj);
fn1(1); // num: 5; anArray: 1,2,3,5; ref.someVar: 4;
fn2(1); // num: 6; anArray: 1,2,3,6; ref.someVar: 4;
obj.someVar++;
fn1(2); // num: 7; anArray: 1,2,3,5,7; ref.someVar: 5;
fn2(2); // num: 8; anArray: 1,2,3,6,8; ref.someVar: 5;

Resumo

Se tudo parece completamente incerto, então a melhor coisa a fazer é brincar com os exemplos. Ler uma explicação é muito mais difícil do que entender exemplos. Minhas explicações de fechamentos e empilhadeiras, etc. não são tecnicamente correctas-são simplificações grosseiras destinadas a ajudar a compreender. Uma vez que a idéia básica é grokked, você pode pegar os detalhes mais tarde.

Final pontos:
  • sempre que utilizar function dentro de outra função, é usado um fecho.
  • sempre que utilizar eval() dentro de uma função, é usado um fecho. O texto que você eval pode referenciar variáveis locais da função, e dentro de eval você pode até mesmo criar novas variáveis locais usando eval('var foo = …')
  • quando você usa new Function(…) (Oconstrutor de funções ) dentro de uma função, ela não cria um fecho. (A nova função não pode referenciar as variáveis locais do função exterior.)
  • um encerramento em JavaScript é como manter uma cópia de todas as variáveis locais, assim como eram quando uma função saiu.
  • é provavelmente melhor pensar que um fechamento é sempre criado apenas uma entrada para uma função, e as variáveis locais são adicionadas a esse fechamento.
  • um novo conjunto de variáveis locais é mantido sempre que uma função com um fecho é chamada (dado que a função contém uma declaração de função dentro dela, e uma referência a isso dentro função é retornada ou uma referência externa é mantida para ela de alguma forma).
  • duas funções podem parecer que têm o mesmo texto de origem, mas têm um comportamento completamente diferente devido ao seu encerramento 'oculto'. Eu não acho que o código JavaScript pode realmente descobrir se uma referência de função tem um encerramento ou não.
  • Se você está tentando fazer qualquer modificação dinâmica do código fonte (por exemplo: myFunction = Function(myFunction.toString().replace(/Hello/,'Hola'));), ele não vai funcionar se myFunction é um fechamento (claro, você nunca iria sequer pense em fazer substituição de código fonte no tempo de execução, mas...).
  • é possível obter declarações de funções dentro das declarações de funções dentro das funções &mdash, e você pode obter encerramentos em mais de um nível.
  • Acho que, normalmente, um encerramento é um termo para ambas as funções, juntamente com as variáveis que são capturadas. Note que eu não uso essa definição neste artigo! Suspeito que os encerramentos em JavaScript diferem dos normalmente encontrados em funções. idioma.

Ligações

  • atributos privados e métodos privados simulados de Douglas Crockfordpara um objecto, usando fechos.
  • uma grande explicação de como os encerramentos podem causar fugas de memória no IE se não tiveres cuidado.
Obrigado.

Se você tiver apenas aprendido fechos (aqui ou em outro lugar!), então eu estou interessado em qualquer feedback de você sobre quaisquer mudanças que você pode sugerir que poderia fazer este artigo é mais claro. Enviar um e-mail para morrisjohns.com (morris_closure @). Por favor, note que eu não sou um guru em JavaScript - nem em fechamentos.


A publicação Original de Morris pode ser encontrada no arquivoda Internet .

 6335
Author: flying sheep, 2018-09-15 09:13:27

Sempre que você vê a palavra-chave da função dentro de outra função, a função interna tem acesso a variáveis na função externa.

function foo(x) {
  var tmp = 3;

  function bar(y) {
    console.log(x + y + (++tmp)); // will log 16
  }

  bar(10);
}

foo(2);

Isto irá sempre registar 16, Porque bar pode aceder ao {[5] } que foi definido como um argumento para foo, e também pode aceder a tmp a partir de foo.

Isso ... é um desfecho. Uma função não tem que retornar a fim de ser chamado de fechamento. Simplesmente aceder a variáveis fora do seu âmbito lexical imediato cria um encerramento.

function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + (++tmp)); // will also log 16
  }
}

var bar = foo(2); // bar is now a closure.
bar(10);

A função acima também irá log 16, Porque bar ainda pode se referir a x e tmp, mesmo que já não esteja diretamente dentro do escopo.

No entanto, uma vez que tmp ainda está por aí dentro do fecho de bar, também está a ser aumentado. Ela será incrementada cada vez que você ligar bar.

A o exemplo mais simples de um fechamento é este:

var a = 10;

function test() {
  console.log(a); // will output 10
  console.log(b); // will output 6
}
var b = 6;
test();

Quando uma função JavaScript é invocada, um novo contexto de execução é criado. Junto com os argumentos da função e o objeto pai, este contexto de execução também recebe todas as variáveis declaradas fora dele (no exemplo acima, tanto 'a' quanto 'b').

É possível criar mais do que uma função de Fecho, quer devolvendo uma lista delas, quer ajustando-as para uma função global. variavel. Todos eles se referirão ao mesmo x e da mesma forma, eles não fazem as suas próprias cópias.

Aqui o número {[5] } é um número literal. Como com outros literais em JavaScript, quando foo é chamado, o número x é copiado em foo como seu argumento x.

Por outro lado, o JavaScript usa sempre referências quando lida com objectos. Se disser, você chamou foo com um objecto, O fecho que ele devolve irá referência que objecto original!

function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + tmp);
    x.memb = x.memb ? x.memb + 1 : 1;
    console.log(x.memb);
  }
}

var age = new Number(2);
var bar = foo(age); // bar is now a closure referencing age.
bar(10);

Como esperado, cada chamada para bar(10) irá aumentar x.memb. O que pode não ser esperado, é que x está simplesmente se referindo ao mesmo objeto que a variável age! Depois de algumas chamadas para bar, age.memb serão dois! Este referenciamento é a base para vazamentos de memória com objetos HTML.

 3826
Author: Ali, 2018-10-10 18:16:11
Prefácio: esta resposta foi escrita quando a pergunta foi:
Como o Velho Albert disse: "Se não o consegues explicar a um miúdo de seis anos, tu próprio não o entendes.". Bem, eu tentei explicar o fechamento JS a um amigo de 27 anos e falhou completamente. Alguém pode considerar que eu tenho 6 anos e estranhamente interessado nesse assunto ?
Tenho quase a certeza que fui uma das únicas pessoas que tentou levar a pergunta inicial à letra. Desde então, a pergunta sofreu várias mutações, então minha resposta pode agora parecer incrivelmente tola e fora do lugar. Esperemos que a ideia geral da história continue a ser divertida para alguns.
Sou um grande fã de analogia e metáfora ao explicar conceitos difíceis, então deixe-me tentar a minha mão com uma história.

Era uma vez:

Havia uma princesa...
function princess() {
Ela vivia num mundo maravilhoso cheio de aventuras. Ela conheceu o seu príncipe encantado, cavalgou pelo seu mundo em um unicórnio, dragões combatidos, encontraram animais falantes, e muitas outras coisas fantásticas.
    var adventures = [];

    function princeCharming() { /* ... */ }

    var unicorn = { /* ... */ },
        dragons = [ /* ... */ ],
        squirrel = "Hello!";

    /* ... */
Mas ela teria sempre de voltar ao seu mundo aborrecido de tarefas e adultos.
    return {
E ela contava-lhes muitas vezes a sua mais recente aventura maravilhosa como princesa.
        story: function() {
            return adventures[adventures.length - 1];
        }
    };
}
Mas tudo o que eles viam era uma menina...
var littleGirl = princess();

...contar histórias sobre magia e fantasia.

littleGirl.story();
E mesmo que os adultos soubessem de verdadeiras princesas, eles saberiam. nunca acredites nos unicórnios ou dragões porque nunca os conseguiam ver. Os adultos disseram que só existiam dentro da imaginação da menina. Mas sabemos a verdade, que a menina com a princesa lá dentro... ...É realmente uma princesa com uma menina lá dentro.
 2273
Author: Jacob Swartwood, 2017-11-01 11:40:09
Levando a questão a sério, devemos descobrir o que uma típica criança de 6 anos é capaz de cognitivamente, embora reconhecidamente, alguém que está interessado em JavaScript não é tão típico. No desenvolvimento da infância: 5 a 7 anos diz:

O seu filho será capaz de seguir instruções em dois passos. Por exemplo, se você disser ao seu filho, "vá para a cozinha e me traga um saco de lixo" eles serão capazes de se lembrar dessa direção.

Podemos usar este exemplo para explicar os encerramentos, como se segue:

A cozinha é um fecho que tem uma variável local, chamada trashBags. Há uma função dentro da cozinha chamada {[2] } que recebe um saco do lixo e devolve-o.

Podemos codificar isto em JavaScript assim:

function makeKitchen() {
  var trashBags = ['A', 'B', 'C']; // only 3 at first

  return {
    getTrashBag: function() {
      return trashBags.pop();
    }
  };
}

var kitchen = makeKitchen();

console.log(kitchen.getTrashBag()); // returns trash bag C
console.log(kitchen.getTrashBag()); // returns trash bag B
console.log(kitchen.getTrashBag()); // returns trash bag A

Outros pontos que explicam porque os encerramentos são interessantes:

  • cada vez que makeKitchen() é chamado, um novo fechamento é criado com o seu próprio separate trashBags.
  • a variável trashBags é local para o interior de cada cozinha e não é acessível para fora, mas a função interna na propriedade getTrashBag tem acesso a ela.
  • cada chamada de função cria um fecho, mas não haveria necessidade de manter o fecho à volta a menos que uma função interna, que tem acesso ao interior do fecho, possa ser chamada de fora do fecho. Devolver o objeto com a função getTrashBag faz isso aqui.
 696
Author: dlaliberte, 2018-10-10 17:50:14

O Homem Palha

Preciso de saber quantas vezes um botão foi clicado e fazer alguma coisa a cada terceiro clique...

Solução Bastante Óbvia

// Declare counter outside event handler's scope
var counter = 0;
var element = document.getElementById('button');

element.addEventListener("click", function() {
  // Increment outside counter
  counter++;

  if (counter === 3) {
    // Do something every third time
    console.log("Third time's the charm!");

    // Reset counter
    counter = 0;
  }
});
<button id="button">Click Me!</button>
Agora isto vai funcionar, mas interfere no escopo externo adicionando uma variável, cujo único propósito é manter o controle da contagem. Em algumas situações, isso seria preferível, uma vez que a sua aplicação externa pode precisar de acesso a esta informação. Mas nisto case, estamos apenas mudando o comportamento de cada terceiro clique, por isso é preferível a incluir esta funcionalidade dentro do manipulador de eventos.

Considere esta opção

var element = document.getElementById('button');

element.addEventListener("click", (function() {
  // init the count to 0
  var count = 0;

  return function(e) { // <- This function becomes the click handler
    count++; //    and will retain access to the above `count`

    if (count === 3) {
      // Do something every third time
      console.log("Third time's the charm!");

      //Reset counter
      count = 0;
    }
  };
})());
<button id="button">Click Me!</button>
Repara em algumas coisas.

No exemplo acima, estou a usar o comportamento de encerramento do JavaScript. Este comportamento permite que qualquer função tenha acesso ao escopo em que foi criada, indefinidamente.Para praticamente aplicar isto, eu ... invocar imediatamente uma função que retorna outra função, e porque a função que estou retornando tem acesso à variável de contagem interna (por causa do comportamento de fechamento explicado acima) isso resulta em um escopo privado para o uso pela função resultante... Não é assim tão simples? Vamos diluí-lo...

Um simples encerramento de uma linha

//          _______________________Immediately invoked______________________
//         |                                                                |
//         |        Scope retained for use      ___Returned as the____      |
//         |       only by returned function   |    value of func     |     |
//         |             |            |        |                      |     |
//         v             v            v        v                      v     v
var func = (function() { var a = 'val'; return function() { alert(a); }; })();

Todas as variáveis fora da função devolvida estão disponíveis para a função devolvida, mas não estão directamente disponíveis para a objecto de função devolvido...

func();  // Alerts "val"
func.a;  // Undefined
Percebeste? Assim, em nosso exemplo primário, a variável de contagem está contida dentro do fechamento e sempre disponível para o tratador de eventos, então ele mantém o seu estado de clique em clique.

Também, esta variável privada estado é totalmente acessível, tanto para leituras como para atribuir as suas variáveis privadas escopadas.

Aqui está, está agora a encapsular completamente este comportamento.

Publicação completa no 'Blog' ( incluindo jQuery considerations)

 537
Author: jondavidjohn, 2017-11-13 04:54:05
Os encerramentos são difíceis de explicar porque são usados para fazer algum comportamento funcionar que todos intuitivamente esperam trabalhar de qualquer maneira. Acho que a melhor maneira de explicá-los (e a maneira que eu aprendi o que eles fazem) é imaginar a situação sem eles:

    var bind = function(x) {
        return function(y) { return x + y; };
    }
    
    var plus5 = bind(5);
    console.log(plus5(3));

O que aconteceria aqui se o JavaScript não soubesse dos encerramentos? Basta substituir a chamada na última linha por seu corpo método (que é basicamente qual função e tu recebes:

console.log(x + 3);
Agora, Onde está a definição de x? Não a definimos no âmbito actual. A única solução é deixar plus5 transportar o seu âmbito (ou melhor, o âmbito do seu pai) em torno. Desta forma, x está bem definido e está ligado ao valor 5.
 450
Author: Konrad Rudolph, 2017-03-17 08:51:15
Esta é uma tentativa de esclarecer vários (possíveis) mal-entendidos sobre encerramentos que aparecem em algumas das outras respostas.
  • um encerramento não é apenas criado quando você retorna uma função interna. de facto, a função envolvente não precisa de regressar de todo para que o seu encerramento seja criado. Você pode, em vez disso, atribuir a sua função interna a uma variável em um escopo exterior, ou passá-la como um argumento para outra função onde ela poderia ser chamada imediatamente ou em qualquer altura mais tarde. Portanto, o fechamento da função envolvente é, provavelmente, criado assim a função envolvente é chamado, uma vez que qualquer função interna tem acesso para que o fechamento sempre que a função mais interna é chamada, antes ou após o delimitador função retorna.
  • um fecho não faz referência a uma cópia dos valores antigos das variáveis no seu âmbito. as próprias variáveis fazem parte do encerramento, e assim o valor visto ao aceder a uma dessas variáveis é o valor mais recente no momento em que é acessado. É por isso que as funções internas criadas dentro de laços podem ser complicadas, uma vez que cada um tem acesso às mesmas variáveis externas ao invés de pegar uma cópia das variáveis no momento em que a função é criada ou chamada.
  • as "variáveis" num Fecho incluem quaisquer funções nomeadas declaradas dentro da função. Eles também incluem argumentos da função. Um fechamento também tem acesso às variáveis de fechamento que contém, até ao âmbito global.
  • os encerramentos usam memória, mas não causam fugas de memória uma vez que o JavaScript por si só limpa as suas próprias estruturas circulares que não são referenciadas. Vazamentos de memória do Internet Explorer envolvendo fechamentos são criados quando ele não consegue desconectar os valores dos atributos DOM que referenciam fechamentos, mantendo assim referências a estruturas possivelmente circulares.
 344
Author: dlaliberte, 2016-05-05 15:00:49
OK, fã de fechamento de 6 anos. Quer ouvir o exemplo mais simples de encerramento? Vamos imaginar a próxima situação: um motorista está sentado num carro. Aquele carro está dentro de um avião. O avião está no aeroporto. A capacidade do condutor de aceder a coisas fora do carro, mas dentro do avião, mesmo que o avião Saia de um aeroporto, é um fecho. É isso. Quando fizer 27 anos, veja a explicação mais detalhadaou o exemplo abaixo. Eis como posso converter a minha história de avião no código.

var plane = function(defaultAirport) {

  var lastAirportLeft = defaultAirport;

  var car = {
    driver: {
      startAccessPlaneInfo: function() {
        setInterval(function() {
          console.log("Last airport was " + lastAirportLeft);
        }, 2000);
      }
    }
  };
  car.driver.startAccessPlaneInfo();

  return {
    leaveTheAirport: function(airPortName) {
      lastAirportLeft = airPortName;
    }
  }
}("Boryspil International Airport");

plane.leaveTheAirport("John F. Kennedy");
 332
Author: Max Tkachenko, 2018-10-10 18:38:01

O encerramento é muito parecido com um objecto. É instintivo sempre que se chama uma função.

O escopo de um encerramento em JavaScript é lexical, o que significa que tudo o que está contido dentro da função o encerramento pertence, tem acesso a qualquer variável que está nele.

Uma variável está contida no fecho SE

  1. atribua-o com var foo=1; ou
  2. escreve apenas var foo;

Se uma função interna (uma função contida dentro de outra função) acede a tal variável sem defini-la em seu próprio escopo com var, ela modifica o conteúdo da variável no fecho exterior .

O encerramento ultrapassa o tempo de execução da função que o gerou. Se outras funções saírem do encerramento/âmbito em que estão definidas (por exemplo, como valores de retorno), estas continuarão a referir que: encerramento .

Exemplo

function example(closure) {
  // define somevariable to live in the closure of example
  var somevariable = 'unchanged';

  return {
    change_to: function(value) {
      somevariable = value;
    },
    log: function(value) {
      console.log('somevariable of closure %s is: %s',
        closure, somevariable);
    }
  }
}

closure_one = example('one');
closure_two = example('two');

closure_one.log();
closure_two.log();
closure_one.change_to('some new value');
closure_one.log();
closure_two.log();

Saída

somevariable of closure one is: unchanged
somevariable of closure two is: unchanged
somevariable of closure one is: some new value
somevariable of closure two is: unchanged
 323
Author: Florian Bösch, 2018-10-10 18:39:20
Escrevi um post no blog a explicar os encerramentos. Aqui está o que eu disse sobre fechamentos em termos de [[2]} Porque você iria querer um.

Os encerramentos são uma forma de deixar uma função ter variáveis persistentes e privadas - ou seja, variáveis que apenas uma função sabe sobre, onde pode manter o registo das informações das horas anteriores que foi executado.

Nesse sentido, deixam uma função agir um pouco como um objecto com atributos privados.

Cheio pós:

O que são estas coisas de encerramento?

 216
Author: Nathan Long, 2013-01-28 02:23:21

Os encerramentos são simples:

O seguinte exemplo simples cobre todos os pontos principais dos encerramentos de JavaScript.*  

Aqui está uma fábrica que produz calculadoras que podem adicionar e multiplicar:
function make_calculator() {
  var n = 0; // this calculator stores a single number n
  return {
    add: function(a) {
      n += a;
      return n;
    },
    multiply: function(a) {
      n *= a;
      return n;
    }
  };
}

first_calculator = make_calculator();
second_calculator = make_calculator();

first_calculator.add(3); // returns 3
second_calculator.add(400); // returns 400

first_calculator.multiply(11); // returns 33
second_calculator.multiply(10); // returns 4000

O ponto-chave: Cada chamada para make_calculator cria uma nova variável local n, que continua a ser utilizável por que da calculadora add e multiply funções muito tempo depois de make_calculator retorna.

Se estiver familiarizado com o stack molduras, estas calculadoras parecem estranhas: como podem continuar a aceder a n Depois de make_calculator regressar? A resposta é imaginar que JavaScript não usa "stack frames", mas em vez disso usa" heap frames", que pode persistir após a chamada de função que os fez retornar.

Funções internas como add e multiply, que acedem variáveis declaradas numa função externa**, são chamados encerramentos .

É praticamente tudo o que há para fazer. encerramento.



* Por exemplo, ele cobre todos os pontos da "Vedantes para Dummies" artigo dada em outra resposta, exceto exemplo 6, que simplesmente mostra que as variáveis podem ser usadas antes de serem declaradas, um bom fato para saber, mas completamente alheios a encerramentos. Ele também cobre todos os pontos em a resposta aceita , Exceto para os pontos (1) que funções copiam seus argumentos em variáveis locais (o nome argumentos de função), e (2) que copiar números cria um novo número, mas copiar uma referência de objeto lhe dá outra referência ao mesmo objeto. Estes também são bons para saber, mas mais uma vez completamente não relacionados com encerramentos. Também é muito semelhante ao exemplo em esta resposta mas um pouco mais curta e menos abstrata. Não cobre o ponto de esta resposta ou este comentário, que é que o JavaScript torna difícil ligar o valor actual de um variável de loop para a sua função interna: o passo" ligar-se " só pode ser feito com uma função auxiliar que encerra a sua função interna e é invocado em cada iteração de loop. (Estritamente falando, a função interna acessa a cópia da função auxiliar da variável, ao invés de ter qualquer coisa conectada.) Novamente, muito útil ao criar fechamentos, mas não parte do que um fechamento é ou como ele funciona. Existe uma confusão adicional devido a fechamentos que funcionam de forma diferente em linguagens funcionais como ML, onde as variáveis são vinculados a valores, em vez de espaço de armazenamento, fornecendo um fluxo constante de pessoas que entendem de tampas em uma forma (a saber, o "ligar" forma) que é simplesmente incorreta para JavaScript, onde as variáveis são sempre vinculado ao espaço de armazenamento, e nunca para os valores.

** Qualquer função externa, se vários forem aninhados, ou mesmo no contexto global, como esta resposta aponta claramente.

 199
Author: Matt, 2017-05-23 12:10:46
Como o explicaria a um miúdo de seis anos? Sabes como os adultos podem ter uma casa e chamam-lhe Casa? Quando uma mãe tem um filho, a criança não tem nada, certo? Mas os pais são donos de uma casa, por isso, sempre que alguém pergunta à criança: "onde é a tua casa?", pode responder " aquela casa!", e apontar para a casa de seus pais. Um "fechamento" é a capacidade da criança para sempre (mesmo que no exterior) ser capaz de dizer que tem um lar, mesmo que é realmente dos pais que possuem o casa.
 196
Author: Magne, 2016-01-16 02:30:44

Pode explicar encerramentos a uma criança de 5 anos?*

Ainda acho que a explicação do Google funciona muito bem e é concisa:

/*
*    When a function is defined in another function and it
*    has access to the outer function's context even after
*    the outer function returns.
*
* An important concept to learn in JavaScript.
*/

function outerFunction(someNum) {
    var someString = 'Hey!';
    var content = document.getElementById('content');
    function innerFunction() {
        content.innerHTML = someNum + ': ' + someString;
        content = null; // Internet Explorer memory leak for DOM reference
    }
    innerFunction();
}

outerFunction(1);​

Proof that this example creates a closure even if the inner function doesn't return

*uma pergunta C#

 187
Author: Chris S, 2017-05-23 11:47:32
Tenho tendência a aprender melhor com comparações boas/más. Gosto de Ver código de trabalho seguido de código não-trabalho que alguém provavelmente encontrará. Eu montei um jsFiddle que faz uma comparação e tenta resumir as diferenças às explicações mais simples que consegui.

Fechos feitos à direita:

console.log('CLOSURES DONE RIGHT');

var arr = [];

function createClosure(n) {
    return function () {
        return 'n = ' + n;
    }
}

for (var index = 0; index < 10; index++) {
    arr[index] = createClosure(index);
}

for (var index in arr) {
    console.log(arr[index]());
}
  • No código acima createClosure(n) é invocado em cada iteração do laço. Note que eu nomeei a variável n para destacar que ela é uma variável Nova criada num novo âmbito de funções e não é a mesma variável que index que está ligada ao âmbito exterior.

  • Isto cria um novo escopo e {[3] } Está ligado a esse escopo; isto significa que temos 10 escopos separados, um para cada iteração.

  • createClosure(n) devolve uma função que devolve o n Dentro desse âmbito.

  • Dentro de cada escopo n Está ligado a qualquer valor que teve quando createClosure(n) foi invocado de modo que a função aninhada que é devolvido irá sempre devolver o valor de n que teve quando createClosure(n) foi invocado.

Fechos feitos de forma errada:

console.log('CLOSURES DONE WRONG');

function createClosureArray() {
    var badArr = [];

    for (var index = 0; index < 10; index++) {
        badArr[index] = function () {
            return 'n = ' + index;
        };
    }
    return badArr;
}

var badArr = createClosureArray();

for (var index in badArr) {
    console.log(badArr[index]());
}
  • No código acima, o loop foi movido dentro da função createClosureArray() e a função agora apenas retorna a matriz completa, que à primeira vista parece mais intuitiva.

  • O que pode não ser óbvio é que desde createClosureArray() só é invocado uma vez que apenas um escopo é criado para esta função em vez de um para cada iteração do laço.

  • Dentro desta função uma variável chamada index é definida. O ciclo corre e adiciona funções ao array que retornam index. Note que index é definido dentro da função createClosureArray que só é invocada uma vez.

  • Como havia apenas um escopo dentro da função createClosureArray(), index só está ligado a um valor dentro desse escopo. Em outras palavras, cada vez que o loop muda o valor de index, Ele o muda para tudo que remete-o para esse âmbito de aplicação.

  • Todas as funções adicionadas ao array retornam a mesma variável index do escopo pai onde foi definido em vez de 10 diferentes de 10 âmbitos diferentes, como o primeiro exemplo. O resultado final é que todas as 10 funções retornam a mesma variável do mesmo escopo.

  • Depois que o loop terminou e index foi feito sendo modificado o valor final era 10, Portanto, cada função adicionada ao array retorna a valor da variável única index que está agora definida como 10.

Resultado

FECHOS FEITOS À DIREITA
n = 0
n = 1
n = 2
n = 3
n = 4
n = 5
n = 6
n = 7
n = 8
n = 9

FECHOS FEITOS DE FORMA ERRADA
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10

 164
Author: Chev, 2017-03-27 17:56:11

Wikipédia em encerramentos:

Em Ciência da computação, um fechamento é uma função juntamente com um ambiente de referência para os nomes Não locais (variáveis livres) dessa função.

Tecnicamente, em JavaScript, cada função é um encerramento. Ele sempre tem acesso a variáveis definidas no escopo circundante.

Uma vez que a construção definidora de escopo em JavaScript é uma função , não um bloco de código como em muitos outros languages, o que normalmente queremos dizer por Fecho em JavaScript é uma função a trabalhar com variáveis não locais definidas na função envolvente já executada.

Os encerramentos são frequentemente usados para criar funções com alguns dados privados ocultos (mas nem sempre é o caso).

var db = (function() {
    // Create a hidden object, which will hold the data
    // it's inaccessible from the outside.
    var data = {};

    // Make a function, which will provide some access to the data.
    return function(key, val) {
        if (val === undefined) { return data[key] } // Get
        else { return data[key] = val } // Set
    }
    // We are calling the anonymous surrounding function,
    // returning the above inner function, which is a closure.
})();

db('x')    // -> undefined
db('x', 1) // Set x to 1
db('x')    // -> 1
// It's impossible to access the data object itself.
// We are able to get or set individual it.

Ems

O exemplo acima está usando uma função anônima, que foi executada uma vez. Mas não tem de ser. Ele pode ser nomeado (por exemplo mkdb) e executado mais tarde, gerar uma função de banco de dados cada vez que é invocado. Cada função gerada terá seu próprio objeto de banco de dados escondido. Outro exemplo de uso de fechamentos é quando não retornamos uma função, mas um objeto contendo várias funções para diferentes propósitos, cada uma dessas funções tendo acesso aos mesmos dados.

 154
Author: mykhal, 2013-12-18 16:48:34
Montei um tutorial de JavaScript interativo para explicar como funcionam os encerramentos. O que é um encerramento? Aqui está um dos exemplos:
var create = function (x) {
    var f = function () {
        return x; // We can refer to x here!
    };
    return f;
};
// 'create' takes one argument, creates a function

var g = create(42);
// g is a function that takes no arguments now

var y = g();
// y is 42 here
 128
Author: Nathan Whitehead, 2014-10-25 22:38:03
As crianças vão lembrar-se sempre dos segredos que partilharam com os pais, mesmo depois dos pais serem desaparecido. É isto que os encerramentos são para funções.

Os segredos das funções JavaScript são as variáveis privadas

var parent = function() {
 var name = "Mary"; // secret
}

Cada vez que lhe chamas, a variável local "name" é criada e o nome próprio "Mary". E cada vez que a função sai a variável é perdida e o nome é esquecido.

Como podes adivinhar, porque as variáveis são re-criado cada vez que a função é chamada, e ninguém mais vai conhecê-los, deve haver um lugar secreto onde eles são armazenados. Pode chamar-se Câmara dos segredos ou pilha ou âmbito local, mas não importa. Sabemos que estão lá, algures, escondidos na memória.

Mas, em JavaScript há uma coisa muito especial que as funções que são criadas dentro de outras funções, também pode conhecer as variáveis locais de seus pais e manter enquanto viverem.

var parent = function() {
  var name = "Mary";
  var child = function(childName) {
    // I can also see that "name" is "Mary"
  }
}

Então, enquanto estivermos na função-mãe, ela pode criar uma ou mais funções-filhos que compartilham as variáveis secretas do lugar secreto.

Mas o mais triste é que, se a criança é também uma variável privada da sua função-mãe, também morreria quando o pai terminasse, e os segredos morreriam com eles. Então, para viver, a criança tem de sair antes que seja tarde demais.
var parent = function() {
  var name = "Mary";
  var child = function(childName) {
    return "My name is " + childName  +", child of " + name; 
  }
  return child; // child leaves the parent ->
}
var child = parent(); // < - and here it is outside 
E agora, apesar de Maria já não ser " correndo", a memória dela não está perdida e seu filho sempre se lembrará de seu nome e outros segredos que compartilharam durante seu tempo juntos. Então, se chamares Alice à criança, ela responderá.
child("Alice") => "My name is Alice, child of Mary"
É tudo o que há para dizer.
 121
Author: Tero Tolonen, 2017-07-13 11:27:32

Não compreendo por que razão as respostas são tão complexas aqui.

Aqui está um encerramento:

var a = 42;

function b() { return a; }
Sim. Deves usar isso muitas vezes por dia.


Não há razão para acreditar que os encerramentos são uma invasão complexa para resolver problemas específicos. Não, os encerramentos são apenas sobre o uso de uma variável que vem de um escopo maior da perspectiva de onde a função foi declarada (não executar) . O que foi? permite que você faça pode ser mais espetacular, ver outras respostas.
 100
Author: floribon, 2015-02-21 23:48:56

Exemplo para o primeiro ponto de dlaliberte:

Um encerramento não é apenas criado quando você retorna uma função interna. De fato, a função envolvente não precisa retornar. Você pode, em vez disso, atribuir sua função interna a uma variável em um escopo exterior, ou passá-la como um argumento para outra função onde ela poderia ser usada imediatamente. Portanto, o fechamento da função envolvente provavelmente já existe no momento em que a função envolvente foi chamada, uma vez que qualquer função tem acesso a ele assim que é chamado.

var i;
function foo(x) {
    var tmp = 3;
    i = function (y) {
        console.log(x + y + (++tmp));
    }
}
foo(2);
i(3);
 87
Author: someisaac, 2016-01-16 02:39:35

Um fecho é onde uma função interna tem acesso a variáveis na sua função externa. Essa é provavelmente a explicação mais simples de uma linha que você pode obter para fechamentos.

 81
Author: Rakesh Pai, 2012-12-24 11:10:56

Eu sei que já existem muitas soluções, mas acho que este pequeno e simples script pode ser útil para demonstrar o conceito:

// makeSequencer will return a "sequencer" function
var makeSequencer = function() {
    var _count = 0; // not accessible outside this function
    var sequencer = function () {
        return _count++;
    }
    return sequencer;
}

var fnext = makeSequencer();
var v0 = fnext();     // v0 = 0;
var v1 = fnext();     // v1 = 1;
var vz = fnext._count // vz = undefined
 81
Author: Gerardo Lima, 2016-05-09 11:32:47
Vais dormir cá e convidas o Dan. Diz ao Dan para trazer um controlador XBox. O Dan convida o Paul. Dan pede a Paul para trazer um controlador. Quantos controladores foram trazidos para a festa?
function sleepOver(howManyControllersToBring) {

    var numberOfDansControllers = howManyControllersToBring;

    return function danInvitedPaul(numberOfPaulsControllers) {
        var totalControllers = numberOfDansControllers + numberOfPaulsControllers;
        return totalControllers;
    }
}

var howManyControllersToBring = 1;

var inviteDan = sleepOver(howManyControllersToBring);

// The only reason Paul was invited is because Dan was invited. 
// So we set Paul's invitation = Dan's invitation.

var danInvitedPaul = inviteDan(howManyControllersToBring);

alert("There were " + danInvitedPaul + " controllers brought to the party.");
 77
Author: StewShack, 2011-07-20 15:16:26

As funções JavaScript podem aceder às suas:

  1. argumentos
  2. locais (isto é, as suas variáveis locais e funções locais)
  3. Ambiente, que inclui::
  • globais, incluindo o DOM
  • Qualquer coisa em funções exteriores

Se uma função acessa o seu ambiente, então a função é um fecho.

Note que as funções externas não são necessárias, embora ofereçam benefícios que não discuto aqui. Ao aceder aos dados na sua ambiente, um fecho mantém esses dados vivos. Na subcase das funções externa / interna, uma função externa pode criar dados locais e, eventualmente, sair, e ainda, se qualquer função interna sobreviver após a saída da função externa, então a função interna(S) mantém vivos os dados locais da função externa. Exemplo de encerramento que utiliza o ambiente global:

Imagine que os Eventos de 'Overflow' da pilha são implementados como encerramentos, voteUp_click e voteDown_click, que têm acesso a variáveis externas é votedup e isVotedDown, que são definidas globalmente. (Por uma questão de simplicidade, estou a referir-me aos botões de voto de StackOverflow, não ao conjunto de botões de voto de resposta.)

Quando o utilizador clica no botão VoteUp, a função voteup_ cllick verifica se está votada == verdadeiro para determinar se deve votar para cima ou simplesmente cancelar um voto para baixo. Função voteup_ cllick é um fecho porque está a aceder à sua ambiente.

var isVotedUp = false;
var isVotedDown = false;

function voteUp_click() {
  if (isVotedUp)
    return;
  else if (isVotedDown)
    SetDownVote(false);
  else
    SetUpVote(true);
}

function voteDown_click() {
  if (isVotedDown)
    return;
  else if (isVotedUp)
    SetUpVote(false);
  else
    SetDownVote(true);
}

function SetUpVote(status) {
  isVotedUp = status;
  // Do some CSS stuff to Vote-Up button
}

function SetDownVote(status) {
  isVotedDown = status;
  // Do some CSS stuff to Vote-Down button
}
Todas estas quatro funções são encerradas à medida que acedem ao seu ambiente.
 73
Author: John Pick, 2016-06-08 22:16:22

O autor de Encerramentos explicou muito bem os encerramentos, explicando a razão pela qual precisamos deles e também explicando o ambiente lexical que é necessário para compreender os encerramentos.
Aqui está o resumo:

E se uma variável for acedida, mas não for local? Como aqui:

Enter image description here

Neste caso, o intérprete Encontra a variável no exterior LexicalEnvironment protesto.

O processo consiste em dois passos:

  1. primeiro, quando uma função f é criada, ela não é criada num vazio espaco. Existe um objecto Lexicalambiental actual. Caso acima, é a janela (a é indefinida no momento da função criacao).

Enter image description here

[[5]] Quando uma função é criada, obtém uma propriedade oculta, chamada [[escopo]], que faz referência ao atual enquadramento lexical.

Enter image description here

Se uma variável é lida, mas não pode ser encontrada em qualquer lugar, um o erro é gerado.

Funções Aninhadas

([5]) as funções podem ser aninhadas umas dentro das outras, formando uma cadeia de ambientes lexicais que também pode ser chamada de cadeia de âmbito.

Enter image description here

Então, a função g tem acesso a g, A E F.

Encerramentos

Uma função aninhada pode continuar a viver após a função externa ter terminado:

Enter image description here

A marcar. Lexical environments:

Enter image description here

Como vemos, this.say é uma propriedade no objecto do utilizador, por isso continua a viver após o utilizador ter terminado.

E se se lembrar, quando this.say é criado, ele (como todas as funções) obtém uma referência interna this.say.[[Scope]] ao actual enquadramento lexical. Assim, o ambiente lexical da execução atual do usuário permanece na memória. Todas as variáveis de usuário também são suas propriedades, por isso eles também são cuidadosamente mantidos, não junked como geralmente.

O objetivo é garantir que, se a função interna quiser acessar uma variável externa no futuro, ela seja capaz de fazê-lo.

Para resumir:

  1. a função interna mantém uma referência ao exterior. Lexicalambiente.
  2. a função interna pode aceder a variáveis a partir dela. a qualquer momento, mesmo que a função exterior esteja terminada.
  3. o navegador mantém o Lexicalambiente e todas as suas propriedades (variáveis) na memória até que haja uma função interior que a faz referência.
Isto chama-se encerramento.
 72
Author: Arvand, 2018-05-14 20:51:20
Como pai de uma criança de 6 anos, actualmente a ensinar crianças pequenas (e um novato relativo a codificar sem educação formal, por isso, serão necessárias correcções), penso que a lição ficaria melhor no jogo prático. Se a criança de 6 anos está pronta para entender o que é um fechamento, então eles são velhos o suficiente para tentar eles mesmos. Sugiro colar o código em jsfiddle.net, explicando um pouco, e deixando-os sozinhos para inventar uma canção única. O texto explicativo abaixo é provavelmente mais apropriado para uma criança de 10 anos.
function sing(person) {

    var firstPart = "There was " + person + " who swallowed ";

    var fly = function() {
        var creature = "a fly";
        var result = "Perhaps she'll die";
        alert(firstPart + creature + "\n" + result);
    };

    var spider = function() {
        var creature = "a spider";
        var result = "that wiggled and jiggled and tickled inside her";
        alert(firstPart + creature + "\n" + result);
    };

    var bird = function() {
        var creature = "a bird";
        var result = "How absurd!";
        alert(firstPart + creature + "\n" + result);
    };

    var cat = function() {
        var creature = "a cat";
        var result = "Imagine That!";
        alert(firstPart + creature + "\n" + result);
    };

    fly();
    spider();
    bird();
    cat();
}

var person="an old lady";

sing(person);

Instruções

Dados: os dados são uma recolha de factos. Pode ser números, palavras, medidas, observações ou até mesmo apenas descrições de coisas. Não lhe podes tocar, cheirar ou provar. Podes escrever, falar e ouvir. Você poderia usá-lo para criar o cheiro e o gosto do toque usando um computador. Ele pode ser útil por um computador usando código.

Código: toda a escrita acima é chamada código . Está escrito em JavaScript.

JAVASCRIPT: JavaScript é uma linguagem. Como o inglês, O francês ou o chinês são línguas. Existem muitas linguagens que são entendidas por Computadores e outros processadores eletrônicos. Para que o JavaScript seja entendido por um computador, ele precisa de um intérprete. Imagine se um professor que só fala russo vem dar a sua aula na escola. Quando o professor diz "все садятся", A turma não entenderia. Mas felizmente tens um aluno russo na tua turma. quem diz isso a toda a gente significa" sentem - se todos", é o que todos fazem. A aula é como um computador e o aluno russo é o intérprete. Para JavaScript o interpretador mais comum é chamado de navegador. Navegador: quando você se conecta à Internet em um computador, tablet ou telefone para visitar um site, você usa um navegador. Exemplos que você pode conhecer São Internet Explorer, Chrome, Firefox e Safari. O navegador pode entender o JavaScript e dizer ao computador o que ele precisa fazer. Forum as instruções são chamadas Funções.

Função: uma função em JavaScript é como uma fábrica. Pode ser uma pequena fábrica com apenas uma máquina lá dentro. Ou pode conter muitas outras pequenas fábricas, cada uma com muitas máquinas fazendo trabalhos diferentes. Numa fábrica de roupas da vida real, pode haver resmas de tecido e bobinas de fios a entrar e T-shirts e jeans a sair. A nossa fábrica JavaScript só processa dados, não pode coser, perfurar um buraco ou derreter metal. Na nossa fábrica JavaScript os dados entram e saem.

Todas estas coisas de dados parecem um pouco chatas, mas é realmente muito legal; podemos ter uma função que diz a um robô o que fazer para o jantar. Digamos que te convido a ti e ao teu amigo para virem a minha casa. Tu gostas mais de pernas de frango, eu gosto de salsichas, o teu amigo quer sempre o que tu queres e o meu amigo não come carne. Não tenho tempo para ir às compras, por isso a função precisa de saber o que temos no frigorífico para tomar decisões. Cada ingrediente tem um tempo de cozinhar diferente e queremos que tudo seja servido quente pelo robô ao mesmo tempo. Nós precisamos fornecer a função com os dados sobre o que nós gostamos, a função poderia 'falar' para o frigorífico, e a função poderia controlar o robô.

Uma função normalmente tem um nome, parêntesis e aparelho. Assim:

function cookMeal() {  /*  STUFF INSIDE THE FUNCTION  */  }

Note que /*...*/ e // parar o código sendo lido pelo navegador.

Nome: pode-se chamar uma função sobre qualquer palavra que se queira. O exemplo "farinha de cozinha" é típico em unir duas palavras e dar à Segunda uma letra maiúscula no início - mas isso não é necessário. Não pode ter um espaço nele, e não pode ser um número por si só.

Parênteses: "parênteses" ou () são a caixa de letras na porta da fábrica de funções JavaScript ou uma caixa de correio na rua Para enviar pacotes de informação para a fábrica. Às vezes, a caixa de correio pode estar marcada , por exemplo. cookMeal(you, me, yourFriend, myFriend, fridge, dinnerTime), nesse caso, você saiba que dados você tem para dar.

Os suspensórios: os" suspensórios " que se parecem com este são os vidros fumados da nossa fábrica. De dentro da fábrica você pode ver para fora, mas de fora você não pode ver para dentro.

O EXEMPLO DO CÓDIGO LONGO ACIMA

O nosso código começa com a palavrafunção , por isso sabemos que é um! Então o nome da função sing - é a minha própria descrição do que é a função. Então parênteses (). Os parênteses estão sempre lá para uma função. Às vezes eles estão vazios, e às vezes eles têm algo dentro. Este tem uma palavra em: Depois disto, há um aparelho como este . Isto marca o início da função sing () . Ele tem um parceiro que marca o fim de sing () Assim }

function sing(person) {  /* STUFF INSIDE THE FUNCTION */  }
Então esta função pode ter algo a ver com cantar, e pode precisar de alguns dados sobre uma pessoa. Tem instruções lá dentro para fazer algo com isso. dado.

Agora, depois da função sing(), perto do fim do código está a linha

var person="an old lady";

Variável: as letras var significam "variável". Uma variável é como um envelope. No exterior, este envelope tem a menção "pessoa". No interior contém um pedaço de papel com a informação que a nossa função precisa, algumas letras e espaços Unidos como um pedaço de cadeia (é chamado de cadeia) que fazem uma frase lendo "uma velha senhora". O nosso envelope pode conter outros tipos de coisas como números( chamados inteiros), instruções (chamadas Funções), listas (chamadas arrays). Porque esta variável é escrita fora de todos os suspensórios {}, e porque você pode ver através das janelas coloridas quando você está dentro dos suspensórios, esta variável pode ser vista em qualquer lugar do Código. Chamamos a isto uma "variável global".

Variável GLOBAL: a pessoa é uma variável global, o que significa que se você mudar o seu valor de "uma senhora idosa "para " a" jovem", a pessoacontinuará a ser um jovem até que você decida mudá-lo novamente e que qualquer outra função no código pode ver que é um jovem. Carregue no botão F12 ou veja a configuração das opções para abrir a consola de desenvolvimento de um navegador e escreva "pessoa" para ver qual é este valor. Tipo person="a young man" para mudá-lo e, em seguida, digite "pessoa" novamente para ver que ele mudou.

Depois disto temos a linha.
sing(person);
Esta linha chama o ... função, como se chamasse um cão

"Vamoscantar , venha e peguepessoa !"

Quando o navegador carregar o código JavaScript an atingiu esta linha, irá iniciar a função. Eu coloquei a linha no final para ter certeza de que o navegador tem toda a informação que precisa para executá-lo.

As funções definem acções-a função principal é cantar. Contém uma variável chamada primeira parte {[25] } que se aplica ao canto sobre a pessoa que se aplica a cada um dos versos da canção: "havia" + pessoa + "que engoliu". Se você digitar firstPart para a consola, você não terá uma resposta porque a variável está trancada em uma função - o navegador não pode ver dentro das janelas coloridas dos suspensórios.

Encerramentos: os encerramentos são as funções menores que estão dentro da função big sing(). As pequenas fábricas dentro da grande fábrica. Cada um tem o seu aparelho. que as variáveis dentro delas não podem ser vistas do lado de fora. É por isso que os nomes das variáveis (criatura e resultado) podem ser repetidos nos encerramentos, mas com valores diferentes. Se você digitar estes nomes variáveis na janela da consola, você não vai obter o seu valor porque ele está escondido por duas camadas de janelas coloridas.

Todos os encerramentos sabem o que é a variável da função sing () {25]} chamada {24]} firstPart {25]}, porque eles podem ver a partir da sua cor janela.

Depois dos encerramentos vêm as linhas.
fly();
spider();
bird();
cat();

A função sing() chamará cada uma destas funções na ordem que lhes é dada. Então o trabalho da função sing() será feito.

 58
Author: grateful, 2016-06-08 22:11:57
OK, falando com uma criança de 6 anos, eu possivelmente usaria seguir associações.
Imagina, estás a brincar com os teus irmãos e irmãs em toda a casa, e estás a mexer-te com os teus brinquedos e trouxeste alguns deles para o quarto do teu irmão mais velho. Passado algum tempo, o teu irmão voltou da escola e foi para o quarto dele, e trancou-o lá dentro, por isso agora já não podias ter acesso aos brinquedos deixados lá de uma forma directa. Mas podias bater à porta e ... pede ao teu irmão Os brinquedos. Isto chama-se Fecho de brinquedos; o teu irmão inventou-o para ti, e agora está no âmbito exterior.
Compare-se com uma situação em que uma porta estava trancada por rascunho e ninguém dentro (execução da função geral), e então algum fogo local ocorre e queima a sala (coletor de lixo: D), e então uma nova sala foi construída e agora você pode deixar Outros brinquedos lá (nova instância de função), mas nunca obter os mesmos brinquedos que foram deixados no primeira instância. Para uma criança avançada, eu colocaria algo como o seguinte. Não é perfeito, mas faz-nos sentir sobre o que é:
function playingInBrothersRoom (withToys) {
  // We closure toys which we played in the brother's room. When he come back and lock the door
  // your brother is supposed to be into the outer [[scope]] object now. Thanks god you could communicate with him.
  var closureToys = withToys || [],
      returnToy, countIt, toy; // Just another closure helpers, for brother's inner use.

  var brotherGivesToyBack = function (toy) {
    // New request. There is not yet closureToys on brother's hand yet. Give him a time.
    returnToy = null;
    if (toy && closureToys.length > 0) { // If we ask for a specific toy, the brother is going to search for it.

      for ( countIt = closureToys.length; countIt; countIt--) {
        if (closureToys[countIt - 1] == toy) {
          returnToy = 'Take your ' + closureToys.splice(countIt - 1, 1) + ', little boy!';
          break;
        }
      }
      returnToy = returnToy || 'Hey, I could not find any ' + toy + ' here. Look for it in another room.';
    }
    else if (closureToys.length > 0) { // Otherwise, just give back everything he has in the room.
      returnToy = 'Behold! ' + closureToys.join(', ') + '.';
      closureToys = [];
    }
    else {
      returnToy = 'Hey, lil shrimp, I gave you everything!';
    }
    console.log(returnToy);
  }
  return brotherGivesToyBack;
}
// You are playing in the house, including the brother's room.
var toys = ['teddybear', 'car', 'jumpingrope'],
    askBrotherForClosuredToy = playingInBrothersRoom(toys);

// The door is locked, and the brother came from the school. You could not cheat and take it out directly.
console.log(askBrotherForClosuredToy.closureToys); // Undefined

// But you could ask your brother politely, to give it back.
askBrotherForClosuredToy('teddybear'); // Hooray, here it is, teddybear
askBrotherForClosuredToy('ball'); // The brother would not be able to find it.
askBrotherForClosuredToy(); // The brother gives you all the rest
askBrotherForClosuredToy(); // Nothing left in there
Como podem ver, os brinquedos deixados no quarto ainda estão acessíveis através do irmão e não importa se o quarto está trancado. Aqui está um jsbin para brincar com ele.
 52
Author: dmi3y, 2014-10-25 22:52:13

Uma resposta para uma criança de seis anos (assumindo que sabe o que é uma função e o que é uma variável, e o que são dados):

As funções podem devolver dados. Um tipo de dados que você pode retornar de uma função é outra função. Quando essa nova função é retornada, todas as variáveis e argumentos usados na função que a criou não desaparecem. Em vez disso, a função pai "fecha."Em outras palavras, nada pode olhar para dentro dele e ver as variáveis que ele usou, exceto para a função que ele indicar. Essa nova função tem uma habilidade especial de olhar para trás dentro da função que a criou e ver os dados dentro dela.

function the_closure() {
  var x = 4;
  return function () {
    return x; // Here, we look back inside the_closure for the value of x
  }
}

var myFn = the_closure();
myFn(); //=> 4

Outra maneira muito simples de explicar é em termos de alcance:

Toda vez que você criar um escopo menor dentro de um escopo maior, o escopo menor será sempre capaz de ver o que está no escopo maior.

 48
Author: Stupid Stupid, 2014-10-25 23:02:19

Uma função em JavaScript não é apenas uma referência a um conjunto de instruções (como na linguagem C), mas também inclui uma estrutura de dados ocultos que é composta de referências a todas as variáveis não locais que usa (variáveis capturadas). Tais funções de duas peças são chamadas de fechamentos. Cada função em JavaScript pode ser considerada um fechamento.

Os encerramentos são funções com um estado. É um pouco semelhante a "isto" no sentido de que "isto" também fornece estado para uma função, mas função e "este" são objetos separados ("este" é apenas um parâmetro extravagante, e a única maneira de ligá-lo permanentemente a uma função é criar um fechamento). Enquanto "isto" e função sempre vivem separadamente, uma função não pode ser separada de seu fechamento e a linguagem não fornece meios para acessar variáveis capturadas.

Porque todas estas variáveis externas referenciadas por uma função listicamente aninhada são na verdade variáveis locais na cadeia das suas funções lexicamente envolventes (variáveis globais) pode ser assumidas como variáveis locais de alguns função de raiz), e a cada execução de uma função cria novas instâncias de suas variáveis locais, segue-se que cada execução de uma função de retorno (ou de outra forma transferir-lo, tais como registrá-lo como uma chamada de retorno) uma função aninhada cria um novo fechamento (com seu próprio potencialmente conjunto exclusivo de referenciados variáveis não-locais, que representam o seu contexto de execução).

Além disso, deve ser entendido que as variáveis locais em JavaScript não são criados no frame da pilha, mas no heap e destruídos apenas quando ninguém os está referenciando. Quando uma função retorna, as referências às suas variáveis locais são diminuídos, mas eles ainda podem ser não-null se durante a execução atual, eles se tornaram parte de um fechamento, e ainda são referenciadas pelo seu léxico funções aninhadas (que só pode acontecer se as referências a estas funções aninhadas foram devolvidos ou de outra forma transferidos para algum código externo).

Exemplo:

function foo (initValue) {
   //This variable is not destroyed when the foo function exits.
   //It is 'captured' by the two nested functions returned below.
   var value = initValue;

   //Note that the two returned functions are created right now.
   //If the foo function is called again, it will return
   //new functions referencing a different 'value' variable.
   return {
       getValue: function () { return value; },
       setValue: function (newValue) { value = newValue; }
   }
}

function bar () {
    //foo sets its local variable 'value' to 5 and returns an object with
    //two functions still referencing that local variable
    var obj = foo(5);

    //Extracting functions just to show that no 'this' is involved here
    var getValue = obj.getValue;
    var setValue = obj.setValue;

    alert(getValue()); //Displays 5
    setValue(10);
    alert(getValue()); //Displays 10

    //At this point getValue and setValue functions are destroyed
    //(in reality they are destroyed at the next iteration of the garbage collector).
    //The local variable 'value' in the foo is no longer referenced by
    //anything and is destroyed too.
}

bar();
 47
Author: srgstm, 2016-05-05 16:04:06
Talvez um pouco mais além de tudo, menos do mais precoce de seis anos, mas alguns exemplos que ajudaram a fazer o conceito de fechamento em JavaScript clicar para mim.

Um fecho é uma função que tem acesso ao escopo de outra função (suas variáveis e funções). A maneira mais fácil de criar um fechamento é com uma função dentro de uma função; a razão é que em JavaScript uma função sempre tem acesso a sua função contendo ambito.

function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        alert(outerVar);
    }
    
    innerFunction();
}

outerFunction();

Alerta: macaco

No exemplo acima, a função exterior é chamada que por sua vez chama innerFunction. Note como o outerVar está disponível para qualquer função inner, evidenciado por sua indicação correta do valor do outerVar.

Agora considere o seguinte:

function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        return outerVar;
    }
    
    return innerFunction;
}

var referenceToInnerFunction = outerFunction();
alert(referenceToInnerFunction());

Alerta: macaco

ReferenceToInnerFunction é definido como outerFunction (), que simplesmente devolve uma referência a innerFunction. Quando referenceToInnerFunction é chamado, ele retorna outerVar. Mais uma vez, como acima, isto demonstra que a função interna tem acesso à função externa, uma variável da função externa. Além disso, é interessante notar que ele mantém este acesso mesmo depois que a função externa terminou de executar.

E aqui é onde as coisas ficam realmente interessantes. Se nos livrássemos da função externa, digamos, defini - la como nula, você poderia pensar que a referenceToInnerFunction perderia o seu acesso ao valor de outerVar. Mas não é esse o caso.

function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        return outerVar;
    }
    
    return innerFunction;
}

var referenceToInnerFunction = outerFunction();
alert(referenceToInnerFunction());

outerFunction = null;
alert(referenceToInnerFunction());
Alerta: macaco. Alerta: macaco Mas como assim? Como pode referenceToInnerFunction ainda saber o valor de outerVar agora que a função externa foi definida como nula?

A razão pela qual o referencetoinfunction ainda pode aceder ao valor de outerVar é porque quando o encerramento foi criado pela primeira vez colocando uma função interna dentro da função externa, a função interna adicionou uma referência a o escopo da função externa (suas variáveis e funções) para a sua cadeia de escopo. O que isso significa é que innerFunction tem um ponteiro ou referência a todas as variáveis de outerFunction, incluindo outerVar. Assim, mesmo quando outerFunction terminar a execução, ou mesmo se ele for excluído ou definido como nulo, as variáveis em seu âmbito, como outerVar, ficar na memória por causa do saldo de referência a elas por parte do innerFunction que tenha sido devolvido para referenceToInnerFunction. Para libertar verdadeiramente outerVar e o resto das variáveis de outerFunction a partir da memória você teria que se livrar desta referência notável a elas, por exemplo, definindo referenceToInnerFunction como null também.

//////////

Há mais duas coisas sobre encerramentos a notar. Em primeiro lugar, o fechamento terá sempre acesso aos últimos valores de sua função contendo.

function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        alert(outerVar);
    }
    
    outerVar = "gorilla";

    innerFunction();
}

outerFunction();
Alerta: gorila Em segundo lugar, quando um encerramento é criado, mantém uma referência a todas as variáveis e funções da sua função envolvente; ela não pode escolher. E assim, os fechamentos devem ser usados com moderação, ou pelo menos com cuidado, pois podem ser intensivos em memória; muitas variáveis podem ser mantidas em memória muito tempo depois de uma função contendo ter terminado de executar.
 46
Author: Michael Dziedzic, 2015-04-29 15:37:06

Eu simplesmente apontaria para a Página de Encerramento do Mozilla . É a melhor, a maior parte da explicação concisa e simples {[[5]} do básico de fechamento e uso prático que eu encontrei. É altamente recomendado para qualquer um que aprende JavaScript.

E sim, eu até recomendaria a uma criança de 6 anos -- se a criança de 6 anos está aprendendo sobre fechamentos, então é lógico que eles estejam prontos para compreender a explicaçãoconcisa e simples fornecida no artigo.

 43
Author: mjmoody383, 2014-10-25 22:54:15