2009-08-26 16 views
32

Ver:acceso desde el exterior variable en bucle de cierre Javascript

for (var i in this.items) { 
    var item = this.items[i]; 
    $("#showcasenav").append("<li id=\"showcasebutton_"+item.id+"\"><img src=\"/images/showcase/icon-"+item.id+".png\" /></li>"); 
    $("#showcasebutton_"+item.id).click(function() { 
     alert(item.id); 
     self.switchto(item.id); 
    }); 
} 

El problema es que el item.id alertado siempre es el ID del último elemento de la matriz (this.items). ¿Cómo resolver?

Respuesta

40

El problema que tenemos aquí es que las variables item cambia con cada bucle. Cuando hace referencia a item en algún momento posterior, se utiliza el último valor que tenía. Puede usar una técnica llamada closure (esencialmente una función que devuelve una función) para ubicar rápidamente la variable de forma diferente.

for (var i in this.items) { 
      var item = this.items[i]; 
      $("#showcasenav").append("<li id=\"showcasebutton_"+item.id+"\"><img src=\"/images/showcase/icon-"+item.id+".png\" /></li>"); 
      $("#showcasebutton_"+item.id).click( 
       // create an anonymous function that will scope "item" 
       (function(item) { 
        // that returns our function 
        return function() { 
        alert(item.id); 
        self.switchto(item.id); 
        }; 
       })(item) // immediately call it with "item" 
      ); 
    } 

Una nota al margen - Veo que tiene jQuery aquí. Tiene una función auxiliar $.each() que se puede usar con matrices, y puede ser un atajo para bucles simples para cada uno. Debido a la forma en que funciona el alcance en esta llamada, no necesitará utilizar un cierre porque "elemento" ya es el parámetro de la función cuando se llamó, no almacenado en un var en el alcance de la función principal, como era cierto en su ejemplo.

$.each(this.items,function(i, item) { 
    $("#showcasenav").append("<li id=\"showcasebutton_"+item.id+"\"><img src=\"/images/showcase/icon-"+item.id+".png\" /></li>"); 
    $("#showcasebutton_"+item.id).click(function() { 
    alert(item.id); 
    self.switchto(item.id); 
    }); 
}); 
+1

Encontré una solución más elegante para usted después del hecho: compruebe la ventaja de usar '$ .each()' – gnarf

+0

¡Gracias! Solo paso un rato tratando de crear un cierre para usar una variable local dentro de mi devolución de llamada por clic. La parte difícil para mí es eliminar los parámetros de la función que se devuelve. – Sam

0

tratar este bucle

for (var i=0; i < this.items.length; i++) { 
    this.items[i] 
}; 
+0

No, el ciclo está bien. El problema es que cada elemento de lista agregado ve el mismo "item.id" en el cierre. –

4

La otra manera de abordar esto es para asegurarse de que el negocio se lleva a cabo de manera efectiva = items[i] llamando a una función. En forma abreviada, esto:

for (var i in this.items) { 
    (function(item) { 
     $("#showcasenav").append("<li id=\"showcasebutton_"+item.id+"\"><img src=\"/images/showcase/icon-"+item.id+".png\" /></li>"); 
     $("#showcasebutton_"+item.id).click(function() { 
      alert(item.id); 
      self.switchto(item.id); 
     }); 
    })(this.items[i]); 
} 

La función anónima que hay un poco desordenado, por lo que es más preferible tener una no tan anónima alrededor para el propósito, pero no el truco.

1

Los cierres Javascript almacenan referencias a sus variables, por lo que todos sus manejadores onclick están usando la misma variable.

lo necesario para capturar la variable en una función intermedia, así:

function buildClickHandler(pageNumber) { 
    return function() { //Create and return a new function 
     alert(item.id); 
     self.switchto(item.id); 
    } 
} 

Entonces, el uso que funcionan para crear click manipuladores, así:

for (var i in this.items) { 
    var item = this.items[i]; 
    $("#showcasenav").append("<li id=\"showcasebutton_"+item.id+"\"><img src=\"/images/showcase/icon-"+item.id+".png\" /></li>"); 

    $("#showcasebutton_"+item.id).click(buildClickHandler(item)); 
} 

Cada llamada a buildClickHandler crea un cierre separado que tiene su propia variable.

0

Sé muy bien que esta es una publicación anterior, pero parece que las personas geniales que diseñan jQuery (que pensé que debías usar) han hecho la solución más óptima para tu problema, según tengo entendido.

En la nueva versión 1.4 de la biblioteca, they have added the jQuery.proxy() function. Esto le permite modificar de manera eficiente el contexto/alcance de la función a la que llama, realizada de manera jQuery, lo que garantiza que puede dejar de utilizar técnicas que podrían arruinar las cosas.

Cuestiones relacionadas