2011-11-21 11 views
6

Jestem programistą C# i używam go do zamykania pracy w C#. Obecnie muszę pracować z anonimowych funkcje JavaScript i mieć problem z następującym fragmencie:Uzyskiwanie dostępu do skopiowanej zmiennej typu integer w anonimowej metodzie javascript

function ClosureTest() { 
    var funcArray = new Array(); 

    var i = 0; 
    while (i < 2) { 
     var contextCopy = i; 

     funcArray[i] = function() { alert(contextCopy); return false; }; 

     i++; 
    } 

    funcArray[0](); 
    funcArray[1](); 
} 

Spodziewam pierwszy funcArray() wywołanie powiedzieć 0 a drugi powiedzieć 1. Jednak obaj mówią: 1. Jak to możliwe?

Pisząc var contextCopy = i, upewniam się, że utworzę kopię i-zmiennej. Następnie w każdej iteracji tworzony jest zupełnie nowy wskaźnik funkcji. Każda funkcja odwołuje się do własnej kopii i, która jest contextCopy. Jednak obie utworzone funkcje z jakiegoś powodu odnoszą się do tej samej contextCopy-zmiennej.

Jak to działa w javascript?

+0

czy istnieje powód, dla którego nie używasz pętli 'for'? – zzzzBov

+1

no there is not ;-) – TwinHabit

Odpowiedz

11

JavaScript ma zamknięcia leksykalne, nie blokuje zamknięć. Mimo, że przypisujesz i do contextCopy, contextCopy jest samo w sobie leksykalnym członkiem ClosureTest (który różni się od C#, gdzie {} daje ci nowy blok z zakresem). Spróbuj tego:

while (i < 2) { 
    funcArray[i] = (function(value) { 
     return function(){ alert(value); return false; } 
    })(i); 
    i++; 
} 
+0

Dziękuję za ten przykład pracy. Po przedstawieniu tych wyjaśnień jest dla mnie jasne, dlaczego moja pierwsza próba nie działa. – TwinHabit

7

Nawiasy klamrowe ({}) w JavaScript nie przechwytują zmiennych tak jak w C#.

Tylko zamknięcia (funkcje) wprowadzają nowy zakres i przechwytują zmienne.

var i = 0; 
while (i < 2) { 
    var contextCopy = i; 
    ... 
} 

jest właściwie interpretować jako:

var i, contextCopy; 
i = 0; 
while (i < 2) { 
    contextCopy = i; 
    ... 
} 

Aby otrzymać kopię zmiennej, trzeba zawinąć kod z zamknięciem:

var i; 
i = 0; 
while (i < 2) { 
    (function (contextCopy) { 
    ... 
    }(i)); 
} 
+1

+1 za wskazanie podnoszenia zmiennej contextCopy (nie było tak jasne, jak w mojej odpowiedzi) – Matt

+0

bardzo dziękuję za wyjaśnienie – TwinHabit

0

nie robić utwórz kopię zmiennej i. Zamiast tego zmieniasz zależność GC od zamknięć, które z niej korzystają. Oznacza to, że gdy pętla while wychodzi, zmienna i dalej żyje w swoim ostatnim stanie (1), a oba zamknięcia odnoszą się do niej.

Inna metoda: zamknięcie zmiennej nie kopiuje jej do zamknięcia (nie ma większego sensu dla obiektów), po prostu powoduje, że twoje zamknięcie jest odniesieniem do zmiennej i zapewnia, że ​​ta zmienna nie jest GCed aż do zamknięcia.

Powiązane problemy