2012-12-11 10 views
31

mam trochę kodu:Jak uniknąć dostępu zmiennej zmienny z zamknięciem

for(var id=0; id < message.receiver.length; id++){ 
    var tmp_id = id; 
    zlib.gzip(JSON.stringify(message.json), function(err, buffer){ 
         ... 
    pushStatusPool[message.receiver[tmp_id]] = null; // fix memory leak 
    delete pushStatusPool[message.receiver[tmp_id]]; 
    ... 
    }); 
} 

I dostał ostrzeżenie, że za pomocą tmp_id w zamknięciu może powodować problemy, ponieważ jest to zmienna zmienny.

Jak mogę tego uniknąć? Mam na myśli, jak mogę wysłać niezmienną zmienną do wywołania zwrotnego, ponieważ jest to pętla for i nie mogę zmienić kodu zlib.gzip? Lub innymi słowy, jak mógłbym przekazać argument do zamknięcia?

+1

Jak można uniknąć czego? Twoje pytanie nie jest jasne. Proszę o wiele więcej szczegółów, z czym potrzebujesz pomocy. – jfriend00

+1

Zacząłem pisać odpowiedź ... ale staje się jasne, że to może użyć refaktora :(Gzipujesz to samo 'message.receiver.length' razy. Opublikuj całą rzecz? – brianreavis

Odpowiedz

41

Musisz utworzyć zakres, aby poprawnie uchwycić tmp_id przy użyciu funkcji samoczynnego wykonywania. To dlatego, że cała pętla for ma jeden zasięg, co oznacza, że ​​za każdym razem przechwytujesz tę samą zmienną. Tak więc wywołanie zwrotne zakończy się błędnymi identyfikatorami, ponieważ wartość temp_id zostanie zmieniona przed wywołaniem wywołania zwrotnego.

Zignorowałbym (lub wyłączyć) ostrzeżenie, które wydaje się narzekać, że ponieważ temp_id jest zmienna, możesz zmienić przypisanie. To głupie. Jeśli naprawdę chcesz to naprawić, spróbuj użyć słowa kluczowego const zamiast var.

for(var id=0; id < message.receiver.length; id++){ 
    (function(){ 
     const tmp_id = id; 
     zlib.gzip(JSON.stringify(message.json), function(err, buffer){ 
         ... 
      pushStatusPool[message.receiver[tmp_id]] = null; // fix memory leak 
      delete pushStatusPool[message.receiver[tmp_id]]; 
      ... 
     }); 
    })(); 
} 
+1

dzięki! Właśnie tego chcę Używam 'webstorm' wyprodukowanego przez JetBrains – bxshi

+4

W swoim kodzie nie przechwytywał żadnej zmiennej javascript ma zasięg funkcji, a nie zakres bloku.Tacka' tmp_id' w obrębie wywołania zwrotnego byłaby różna w zależności od tego, kiedy została wykonana. I w twoim przykładzie słowo kluczowe "const" nie jest naprawdę potrzebne ... 'var' zrobiłoby lewę :) – brianreavis

+0

@brianreavis Dobry punkt o czasie wykonania. Nie zauważyłem, że 'gzip' będzie wykonywany asynchronicznie, dlatego zakres funkcji jest kluczowy. Dokona edycji. Nie wiem nic o Webstormie, ale bez 'const' OP nadal przechwytuje zmienną zmienną (tj. Mogę zmienić' tmp_id' po wywołaniu 'gzip') i to * brzmi * tak jak to narzeka ostrzeżenie. –

10

Mam wobec tego samego problemu i rozwiązać go nieznacznie modyfikując odpowiedź user24359, przekazując identyfikator do zamknięcia:

for(var id=0; id < message.receiver.length; id++){ 
    (function(tmp_id){ 
     zlib.gzip(JSON.stringify(message.json), function(err, buffer){ 
         ... 
      pushStatusPool[message.receiver[tmp_id]] = null; // fix memory leak 
      delete pushStatusPool[message.receiver[tmp_id]]; 
      ... 
     }); 
    })(id); 
} 
+0

Podoba mi się to podejście – rupps

2

tu uproszczenie wielką odpowiedź user24359 użytkownika. To rozwiązanie:

var object = {a:1,b:2}; 

for (var y in object){ 
    (function(){const yyy = y; 
     setTimeout(function(){console.log(yyy)},3000);})(); 
} 

Powyższy kod rejestruje B i jest rozwiązaniem. następujące dzienniki kod b b:

var object = {a:1,b:2}; 
for (var y in object){ 

    setTimeout(function(){console.log(y)},3000); 
} 
+0

To było znacznie łatwiejsze do zrozumienia, dzięki –

0

Mam wobec tego samego problemu w kątomierz. Rozwiązać go za pomocą następujący kod -

(function(no_of_agents){ 
       ptor.element.all(by.repeater('agent in agents').column('displayName')).then(function(firstColumn){ 
        console.log(i, '>>>>>Verifying the agent Name'); 
        var agentsSorted = sortAgentsByName(); 
        //verify the agent name 
        expect(firstColumn[no_of_agents].getText()).toEqual(agentsSorted[no_of_agents].name); 
        //now click on the agent name link 
        firstColumn[no_of_agents].click(); 
        ptor.sleep(5000); 
       }); 
      })(no_of_agents); 
0

@ user24359 odpowiedź jest to dobre rozwiązanie, ale można po prostu zastąpić słowa kluczowego var słowem kluczowym let.

for(var id=0; 

staje

for(let id=0; 

Zobacz szczegóły here.

Edit: Jak sugeruje to Heriberto Juárez, to działa tylko dla przeglądarek obsługiwanych EcmaScript6.

+1

Czy nie byłoby to przyczyną problemów w przeglądarkach, które nie obsługują ECMAScript6? –

0
for (var id = 0; id < message.receiver.length; id++) { 
    var tmp_id = id; 
    zlib.gzip(JSON.stringify(message.json), (err, buffer) => { 
    // Something with tmp_id ... 
    }); 
} 

Tworzenie zamknięć pętli z var (tmp_id) mieści się w górnym zakresie funkcji połączenia zwrotnego jest common mistake że należy unikać ze względu na var nie jest w bloku o zakresie. Z tego powodu i ponieważ każde zamknięcie utworzone w pętli ma ten sam lexical environment, zmienna będzie zawsze ostatnią iterowaną wartością (tj. message.receiver.length-1 jako tmp_id) po wywołaniu funkcji wywołania zwrotnego.Twój IDE wykrywa to zachowanie i narzeka słusznie.

Aby uniknąć ostrzeżenie, istnieje wiele rozwiązań:

  • wymienić var z let (varlet tmp_id = id) zapewnienie każdy utworzony zamknięcia mieć własne zawężona tmp_id określone w każdej iteracji:

    for (var id = 0; id < message.receiver.length; id++) { 
        let tmp_id = id; 
        zlib.gzip(JSON.stringify(message.json), (err, buffer) => { 
        // Do something with tmp_id ... 
        }); 
    } 
    
  • Twórz środowisko leksykalne w każdą iterację, wykorzystując IIEF, np. gennadi.w did.

  • Tworzenie oddzwonienia funkcję w każdej iteracji za pomocą funkcji Factory (createCallback):

    const createCallback = tmp_id => (err, buffer) => { 
        // Do something with tmp_id ... 
    }; 
    for (var id = 0; id < message.receiver.length; id++) { 
        zlib.gzip(JSON.stringify(message.json), createCallback(id)); 
    } 
    
  • Bind zmienna (e) w funkcji wywołania zwrotnego, w którym uzyskać poprzedzany do jego parametrów:

    for (var id = 0; id < message.receiver.length; id++) { 
        zlib.gzip(JSON.stringify(message.json), function(tmp_id, err, buffer) { 
        // Do something with tmp_id (passed as id) ... 
        }.bind(this, id)); 
    } 
    

    Uwaga: jest to możliwe tylko na function s. Funkcje strzałek (=>) nie obsługują bind.


Generalnie var należy unikać (od ECMAScript 2015) z powodu takich zachowań błędów.

Powiązane problemy