2011-01-23 12 views
7

He visto programadores asignar eventos oyentes dentro de bucles, utilizando el contador. Creo que esta es la sintaxis:Explique los cierres o enlace del contador de bucles al alcance de la función

for(var i=0; i < someArray.length; i++){ 
    someArray[i].onclick = (function(i){/* Some code using i */})(i); 
} 

Podría alguien explicar la lógica detrás de esto, y esta sintaxis extraña, nunca he visto esto:

(function(i))(i); 

Muchas gracias por su tiempo y paciencia.

+3

Esto técnicamente no implica el cierre. Aunque no estoy del todo seguro de lo bueno que es; esa función interna se ejecutará inmediatamente, y a menos que devuelva otra función, el onclick se establecerá en algo realmente extraño. – cHao

+0

@cHao ya que esto, llamada de función anónima inmediata, se asigna a onlick, debe devolver una función, por lo que la función devuelta es un cierre con la variable "i" en el entorno circundante. – jcubic

+0

@jcubic: * Si el código está configurando un manejador de eventos DOM *, debe devolver una función * si funciona correctamente *. No hay suficiente código aquí para justificar cualquiera de las suposiciones. – cHao

Respuesta

5

Esto se hace porque JavaScript solo tiene alcance de función, no alcance del bloque. Por lo tanto, cada variable que declare en un bucle está en el alcance de la función y cada cierre que cree tiene acceso a la misma variable.

Así que la única manera de crear un nuevo ámbito es llamar a una función y eso es lo que

(function(i){/* Some code using i */}(i)) 

está haciendo.

en cuenta que su ejemplo se pierde una parte importante: La función inmediata tiene que volver otra función que será el manejador de click:

someArray[i].onclick = (function(i){ 
    return function() { 
     /* Some code using i */ 
    } 
}(i)); 

La función inmediata es nada especial. De alguna manera, está definiendo la definición de función y la llamada a función. Puede reemplazarlo por una llamada a función normal:

function getClickHandler(i) { 
    return function() { 
     /* Some code using i */ 
    } 
} 

for(var i=0; i < someArray.length; i++){ 
    someArray[i].onclick = getClickHandler(i); 
} 
+0

Gracias, lo tengo :) – Dean

+0

+1 para la versión 'getClickHandler', que es un mejor enfoque IMO. – user113716

7

La sintaxis (function(i))(i) crea una función anónima y luego la ejecuta de inmediato.

Normalmente, hará esto para crear una función nueva cada vez que pasa el ciclo, que tiene su propia copia de la variable en lugar de que cada controlador de eventos comparta la misma variable.

Así, por ejemplo:

for(int i = 0; i < 10; i++) 
    buttons[i].click = function() { doFoo(i); }; 

normalmente toma a la gente, porque no importa lo que se hace clic en el botón, se llama doFoo(10).

Considerando lo siguiente:

for(int i = 0; i < 10; i++) 
    buttons[i].click = (function(i){ return function() { doFoo(i); };)(i); 

crea una nueva instancia de la función interna (con su propio valor de i) para cada iteración, y funciona como se esperaba.

+0

¿Entonces inserta la función que desea asignar al evento particular dentro de la función anónima, y ​​el valor del "contador" en ese punto se mantiene dentro del alcance de las nuevas funciones? – Dean

+1

@Dean: He editado algunos ejemplos de código en mi respuesta. ¿Ayudan? –

+1

@Dean sí, y funciona porque los argumentos están ** copiados ** cuando se pasan a funciones (para primitivas). –

Cuestiones relacionadas