Como funciona uma function em um loop (que retorna outra function)?

Eu tenho tentado atribuir uma function ao evento onclick de uma tag “a” criada dinamicamente em JavaScript. Todas as tags são criadas em um loop da seguinte maneira:

for ( var i = 0; i < 4; i++ ) { var a = document.createElement( "a" ); a.onclick = function( ) { alert( i ) }; document.getElementById( "foo" ).appendChild( a ); } 

O valor alertado para todos os quatro links é sempre “4”. Muito obvio. Quando pesquisei, encontrei uma postagem que mostra o seguinte trecho de código:

 a.onclick = (function(p, d) { return function(){ show_photo(p, d) } })(path, description); 

Eu consegui ajustá-lo para as minhas necessidades e consegui que a coisa do alerta (i) funcionasse corretamente, mas eu apreciaria se alguém pudesse explicar exatamente o que o código acima faz.

Quando você atribui a function ao manipulador de cliques, um fechamento é criado.

Basicamente, um encerramento é formado quando você aninha funções, funções internas podem se referir às variables ​​presentes em suas funções externas, mesmo após suas funções pai já terem sido executadas.

No momento em que o evento click é executado, o manipulador refere-se ao último valor que a variável i tinha, porque essa variável é armazenada no encerramento.

Como você percebeu, ao envolver a function de manipulador de clique para aceitar a variável i como um argumento e retornar outra function (basicamente criar outro fechamento), ela funciona conforme o esperado:

 for ( var i = 0; i < 4; i++ ) { var a = document.createElement( "a" ); a.onclick = (function(j) { // a closure is created return function () { alert(j); } }(i)); document.getElementById( "foo" ).appendChild( a ); } 

Quando você itera, realmente cria 4 funções, cada function armazena uma referência a i no momento em que foi criada (passando por i ), este valor é armazenado no fechamento externo e a function interna é executada quando o evento click é acionado.

Eu uso o seguinte trecho para explicar closures (e um conceito muito básico de curry ), eu acho que um exemplo simples pode tornar mais fácil obter o conceito:

 // a function that generates functions to add two numbers function addGenerator (x) { // closure that stores the first number return function (y){ // make the addition return x + y; }; } var plusOne = addGenerator(1), // create two number adding functions addFive = addGenerator(5); alert(addFive(10)); // 15 alert(plusOne(10)); // 11 

Sem entrar em muitos detalhes, isso essencialmente cria cópias das variables ​​de instância envolvendo-as em uma function que é executada imediatamente e passa de volta para a function que será executada quando o elemento for clicado.

Pense nisso assim:

 function() { alert(i); } // Will expose the latest value of i (function(I) { return function() { alert(I); }; })(i); // Will pass the current // value of i and return // a function that exposes // i at that time 

Portanto, durante cada iteração do loop, você está realmente executando uma function que retorna uma function com o valor atual da variável.

Que, se você imaginar que tem 4 âncoras no seu loop, você está criando 4 funções separadas que podem ser visualizadas como …

 function() { alert(0); }; function() { alert(1); }; function() { alert(2); }; function() { alert(3); }; 

Eu consideraria olhando no escopo e encerramentos com javascript como se você vá por este caminho e não entender exatamente o que está acontecendo, você pode executar em grandes problemas de comportamento inesperado.

Quando o evento onclick é acionado, a function anônima é chamada e se refere à mesma variável i que foi usada no loop e contém o último valor de i , que é 4.

A solução para o seu problema é usar uma function que retorna uma function:

 a.onclick = (function(k) {return function() { alert(k); }; })(i);