2013-07-16 13 views
7

prega di consultare la jsfiddle: http://jsfiddle.net/LsNCa/2/JavaScript- scoping ed evento gestore variabile

function MyFunc() { 

    for (var i = 0; i < 2; i++) { // i= 0, 1 
     var myDiv = $('<div>'); 

     myDiv.click(function(e) { 
      alert(i); // both the two divs alert "2", not 0 and 1 as I expected 
     }); 
     $('body').append(myDiv); 
    } 
} 

var myFunc = new MyFunc(); 

Voglio che il div per avvisare "0" e "1", rispettivamente, quando clicco loro. Ma entrambi avvertono "2".

Quando faccio clic sui div e l'evento viene attivato, come e dove il gestore trova il valore della variabile i?

Sono consapevole che l'aggiunta di una chiusura raggiunge il mio obiettivo. Ma perché?

+0

possibile duplicato di [Javascript infamous loop problem?] (Http://stackoverflow.com/questions/1451009/javascript-infamous-loop-problem) – elclanrs

risposta

7
function MyFunc() { 

    for (var i = 0; i < 2; i++) { // i= 0, 1 
     (function(j) { 
      var myDiv = $('<div>'); 

      myDiv.click(function(e) { 
       alert(j); 
      }); 
      $('body').append(myDiv); 
     })(i); 
    } 
} 

var myFunc = new MyFunc(); 

Il codice sopra riportato indica come farlo funzionare correttamente. Senza una chiusura, tu sei sempre l'ultimo valore di i. Quello che facciamo è postare io nella chiusura e lasciare che il runtime "ricordi" il valore di quel momento.

+0

È questo il modo standard per farlo? Se il corpo del loop è abbastanza lungo, lo avvolgiamo ancora all'interno di una funzione anonima? – Boyang

+0

Questo è il solito idioma. Ma potresti usare lo stile di Zenwolf se preferisci funzioni più lunghe. – Barmar

+0

Come posso vedere le persone di solito lo scrivono in questo modo. prova a non farlo in un grande ciclo. Voglio dire per (var i = 0; i <1000; i ++). – yaoxing

2

È necessaria una chiusura poiché tutte le funzioni del gestore di eventi fanno riferimento alla stessa variabile i. Il ciclo for lo aggiorna e, quando il ciclo è terminato, la variabile contiene 2. Quindi, quando qualcuno fa clic su uno dei DIV, accede a tale variabile.

Per risolvere questo problema, ogni gestore di eventi deve essere una chiusura con una propria variabile i che contiene un'istantanea del valore al momento della creazione della chiusura.

+0

Puoi scrivere del codice? A scopo illustrativo. :) – Boyang

+0

Hai detto di conoscere il modo corretto di scriverlo, volevi solo una spiegazione del perché è necessario. Non sono sicuro di quale codice posso scrivere per illustrare la risposta: il tuo codice funziona bene. – Barmar

1

suggerisco di leggere this article

JavaScript paranchi dichiarazioni. Ciò significa che entrambe le istruzioni var e le dichiarazioni di funzione verranno spostate nella parte superiore del loro ambito di inclusione .

Come ha detto @Barmar nella sua risposta sopra, la variabile i viene referenziata da entrambi i gestori di eventi.

Evitare di dichiarare le funzioni all'interno dei loop. Di seguito c'è un codice che fa ciò di cui hai bisogno.

Suppongo che tu stia utilizzando jQuery.

function MyFunc() { 

    for (var i = 0; i < 2; i++) { // i= 0, 1 
     var myDiv = $('<div>'); 

     $('body').append(myDiv); 
    } 
    $('div').on('click', function() { 
     alert($(this).index()); 
    }); 
} 

var myFunc = new MyFunc(); 
0

Il "alert()" chiamata succede dopo il ciclo for completato, il che significa che il valore di "i" sarà l'ultimo valore per qualsiasi cosa dopo. Al fine di cogliere i valori individuali di "i", è necessario creare una chiusura per ogni valore attraverso la creazione di una nuova funzione:

function MyFunc() { 

    function alertFn(val) { 
     return function() { 
      alert(val); 
     }; 
    } 

    for (var i = 0; i < 2; i++) { 
     var myDiv = $('<div>'); 
     myDiv.click(alertFn(i)); 
     $('body').append(myDiv); 
    } 
} 

var myFunc = new MyFunc(); 

La chiusura cattura il valore di "i" al momento è stato passato alla funzione , consentendo a alert() di mostrare il valore che ti aspetti.