21

He intentado asignar una función al evento onclick de una etiqueta "a" creada dinámicamente en JavaScript. Todas las etiquetas se crean en un bucle de la siguiente manera:¿Cómo funciona una función en un bucle (que devuelve otra función)?

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

El valor alertado para los cuatro enlaces siempre es "4". Bastante obvio. Cuando buscando en Google me encontré con un post que muestra el siguiente fragmento de código:

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

logré retocarlo para mis necesidades y me la alerta (i) que funcione correctamente, pero Voy a apreciar si alguien podría explicar exactamente lo que hace el código anterior

+0

Hola, ¿Me podría decir por qué alerta "4"? ¿No debería ser "2"? Gracias. – Tarik

+0

for (var i = 0; i <3; i ++) deja i == 4 al final –

+1

No, me deja como 3. –

Respuesta

45

Cuando se asigna la función al controlador de clic, se crea un closure.

Básicamente se forma un cierre cuando se anidan funciones, las funciones internas pueden referirse a las variables presentes en sus funciones externas de inclusión incluso después de que sus funciones principales ya se hayan ejecutado.

En el momento en que se ejecuta el evento click, el controlador hace referencia al último valor que tenía la variable i, porque esa variable se almacena en el cierre.

Como habrá notado, envolviendo la función de controlador click con el fin de aceptar la variable i como un argumento, y volviendo otra función (básicamente crear otro cierre) funciona como se espera:

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); 
} 

Cuando iterar, en realidad crear 4 funciones, cada función almacenar una referencia a i en el momento en que se creó (pasando i), este valor se almacena en el cierre exterior y la función interna se ejecuta cuando el evento click se dispara.

utilizo el siguiente fragmento de explicar los cierres (y un concepto muy básico de curry), creo que un ejemplo simple puede hacer más fácil conseguir el concepto:

// 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 
+3

Taylor, estoy feliz por ti, te dejaré terminar, pero esta publicación es la mejor de todos los tiempos, de todos los tiempos. – Tarik

+0

No es de extrañar que tengas 42k puntos :) –

+4

Un día, la gente se habrá olvidado de Kanye West, como yo, y todos los memes que se refieren a él saldrán como algo grosero y un poco extraño. – RandomInsano

10

Sin entrar en demasiados detalles, esto esencialmente crea copias de las variables de instancia envolviéndolas en una función que se ejecuta inmediatamente y pasa el reverso a la función que se ejecutará cuando se haga clic en el elemento.

Piense en ello como esto:

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 

Así que durante cada iteración del bucle que son en realidad ejecutar una función que devuelve una funcióncon la corriente valor de la variable.

Lo cual, si se imagina que tienes 4 anclajes en el bucle que está creando 4 funciones separadas que se pueden visualizar como ..

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

que sería considerar el mirar en alcance y cierres con javascript como si sigue por este camino y no entiendes exactamente lo que está sucediendo, puedes enfrentarte a problemas masivos debido a un comportamiento inesperado.

+0

Gracias por la explicación. Es un buen truco, ¿o es así como se hace en javascript? – Amarghosh

2

Cuando se activa el evento onclick, la función anónima se llama y se refiere a la misma variable i que se utilizó en el bucle y que mantiene el último valor de i, es decir 4.

La solución a su problema es utilizar una función que devuelve una función:

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