2009-11-04 14 views
23

Rozważmy taką pętlę:JavaScript zmienny wiązanie i pętla

for(var it = 0; it < 2; it++) 
{ 
    setTimeout(function() { 
     alert(it); 
    }, 1); 
} 

wyjście jest:

=> 2 
=> 2 

chciałbym go mieć: 0, 1. Widzę dwa sposoby, aby je naprawić:

Rozwiązanie # 1.

Ten oparty na fakcie, że możemy przekazać dane do setTimeout.

for(var it = 0; it < 2; it++) 
{ 
    setTimeout(function(data) { 
     alert(data); 
    }, 1, it); 
} 

Rozwiązanie nr 2.

function foo(data) 
{ 
    setTimeout(function() { 
     alert(data); 
    }, 1); 
} 

for(var it = 0; it < 2; it++) 
{ 
    foo(it); 
} 

Czy istnieją jakieś inne alternatywy?

Odpowiedz

42

Nie bardzo coś więcej niż dwóch sposobów, które zostały zaproponowane, ale oto kolejny

for(var it = 0; it < 2; it++) 
{ 
    (function() { 
     var m = it; 
     setTimeout(function() { 
      alert(m); 
     }, 1); 
    })(); 
} 

Zasadniczo trzeba uchwycić wartość zmiennej w zamknięciu. Ta metoda używa natychmiast wywoływanej funkcji anonimowej do przechwytywania wartości zmiennej zewnętrznej it w zmiennej lokalnej .

Oto do zabawy. dodać /edytować do adresu URL, aby zobaczyć kod

+4

+1. Możesz jednak nieco to zmienić, zmieniając podpis metody na: 'function (m) {/ * code * /}) (it);' – Alan

+0

+1, ale czy ktoś może mi wyjaśnić, dlaczego to działa ?! –

+1

@digorydoo Funkcja zadeklarowana w pętli jest zawijana w nawiasy, po których następuje zestaw nawiasów, które natychmiast wywołują funkcję. Ponieważ zmienne są ograniczone do funkcji, w której są zadeklarowane (lub zakres globalny, jeśli nie zostały zadeklarowane w ramach funkcji), wartość 'it' w każdej iteracji jest przypisana do zmiennej' m', która jest ograniczona do funkcji, która jest wykonywana natychmiast. –

1

Podobny do powyższego rozwiązania, ale samo powołując się wewnątrz funkcji setTimeout

for(var it = 0; it < 2; it++) 
{ 
    setTimeout(function(cur) { 
     return function(){ 
      alert(cur); 
     }; 
    }(it), 1); 
} 
7

ze słowem kluczowym let można obejść ten problem całkowicie:

for(let it = 0; it < 2; it++) 
{ 
    setTimeout(function() { 
     alert(it); 
    }, 1); 
} 
+0

ale nie ma słowa kluczowego o nazwie 'let' w javascriptu –

1

Podobny do innych rozwiązań, ale moim zdaniem czystsze:

for (var it = 0; it < 2; it++) { 
    // Capture the value of "it" for closure use 
    (function(it) { 
    setTimeout(function() { 
     alert(it); 
    }, 1); 
    // End variable captured code 
    })(it) 
} 

Zachowuje tę samą nazwę zmiennej dla przechwytywania i robi to dla całej pętli, oddzielając ją od logiki konfiguracji limitu czasu. Jeśli chcesz dodać więcej logiki do bloku, możesz to zrobić w trywialny sposób.

Jedyne, co mi się nie podoba w tym rozwiązaniu, to powtórzenie "to" na końcu.