Pregunta

    

Esta pregunta ya tiene una respuesta aquí:

    
            
  •              JavaScript - sencilla práctica ejemplo                                      44 respuestas                          
  •     
    

Esta es una versión simplificada de algo que estoy tratando de correr:

for (var i = 0; i < results.length; i++) {
    marker = results[i];
    google.maps.event.addListener(marker, 'click', function() { 
        change_selection(i);
    }); 
}

pero me estoy encontrando que cada oyente utiliza el valor de results.length (el valor cuando el bucle termina a). ¿Cómo puedo añadir detectores de tal manera que cada uno utiliza el valor de i en el momento añado que, en lugar de la referencia a la i?

¿Fue útil?

Solución

En los navegadores modernos, se pueden utilizar las let o const palabras clave para crear una variable con ámbito de bloque:

for (let i = 0; i < results.length; i++) {
  let marker = results[i];
  google.maps.event.addListener(marker, 'click', () => change_selection(i));
}

En los navegadores más antiguos, es necesario crear un ámbito separado que guarda la variable en su estado actual pasándolo como un parámetro de función:

for (var i = 0; i < results.length; i++) {
  (function (i) {
    marker = results[i];
    google.maps.event.addListener(marker, 'click', function() { 
      change_selection(i);
    }); 
  })(i);
}

Al crear una función anónima y llamándola con la variable como primer argumento, estás pasando por valor de la función y la creación de un cierre.

Otros consejos

Además de los cierres, puede utilizar function.bind:

google.maps.event.addListener(marker, 'click', change_selection.bind(null, i));

pasa el valor de i en como un argumento de la función cuando se le llama. (null es para this, lo que no es necesario en este caso vinculante.)

function.bind fue introducido por el marco de prototipos y se ha estandarizado en ECMAScript Quinta edición. Hasta navegadores todo el soporte de forma nativa, se puede añadir su propio apoyo function.bind utilizando cierres:

if (!('bind' in Function.prototype)) {
    Function.prototype.bind= function(owner) {
        var that= this;
        var args= Array.prototype.slice.call(arguments, 1);
        return function() {
            return that.apply(owner,
                args.length===0? arguments : arguments.length===0? args :
                args.concat(Array.prototype.slice.call(arguments, 0))
            );
        };
    };
}

cierres:

for (var i = 0, l= results.length; i < l; i++) {
    marker = results[i];
    (function(index){
        google.maps.event.addListener(marker, 'click', function() { 
            change_selection(index);
        }); 
    })(i);
}

EDITAR, 2013: Estos ahora se denominan comúnmente como una IIFE

Esta disolución con un cierre. He aquí un artículo sobre los cierres y cómo trabajar con ellos. Salida Ejemplo 5 en la página; ese es el escenario que estamos tratando.

EDIT: Cuatro años después, ese enlace está muerto. La raíz del problema anterior es que los cierres forma el lazo for (específicamente en marker = results[i]). A medida que se pasa en marker addEventListener, se ve el efecto secundario del cierre: la compartida "medio ambiente" se actualiza con cada iteración del ciclo, antes de que finalmente se "salvó" a través del cierre después de la iteración final. MDN explica esto muy bien.

for (var i = 0; i < results.length; i++) {
    marker = results[i];
    google.maps.event.addListener(marker, 'click', (function(i) {
        return function(){
            change_selection(i);
        }
    })(i)); 
}

Creo que podemos definir una variable temporal para almacenar el valor de i.

for (var i = 0; i < results.length; i++) {
 var marker = results[i];
 var j = i;
 google.maps.event.addListener(marker, 'click', function() { 
   change_selection(j);
 }); 
}

No he probado sin embargo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top