JavaScript closures vs. anonymous functions

Mój przyjaciel i ja obecnie dyskutujemy, co jest zamknięciem w JS, a co nie. chcemy tylko upewnić się, że naprawdę rozumiemy to poprawnie.

Weźmy ten przykład. Mamy pętlę liczenia i chcemy wydrukować zmienną counter na konsoli z opóźnieniem. Dlatego używamy setTimeout i zamknięć do przechwytywania wartości zmiennej counter, aby upewnić się, że nie wyświetli ona N razy wartości n.

Złe rozwiązanie bez zamknięć lub wszystko blisko będzie:

for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i);
    }, 1000);
}

, który oczywiście wydrukuje 10 razy wartość i po pętli, czyli 10.

Więc jego próba była:

for(var i = 0; i < 10; i++) {
    (function(){
        var i2 = i;
        setTimeout(function(){
            console.log(i2);
        }, 1000)
    })();
}

Drukowanie od 0 do 9 zgodnie z oczekiwaniami.

Powiedziałam mu, że nie używa zamknięcia do schwytania i, ale on nalega, że jest. Udowodniłem, że nie używa zamknięć umieszczając ciało pętli for wewnątrz innego setTimeout (przekazując swoją anonimową funkcję setTimeout), drukowanie 10 razy 10 ponownie. To samo dotyczy, jeśli przechowuję jego funkcję w var i wykonuję ją po pętli, również drukując 10 razy 10. Więc moim argumentem jest to, że on naprawdę nie uchwycić wartość i, czyniąc jego wersję, a nie zamknięciem.

Moja próba była:

for(var i = 0; i < 10; i++) {
    setTimeout((function(i2){
        return function() {
            console.log(i2);
        }
    })(i), 1000);
}

Więc przechwytuję i (nazwane i2 w ramach zamknięcia), ale teraz zwracam inną funkcję i przekazuję to dookoła. W moim przypadku, funkcja przekazywana do setTimeout naprawdę przechwytuje i.

Kto używa zamknięć, a kto nie?

Zauważ, że oba rozwiązania drukują od 0 do 9 na konsoli z opóźnieniem, więc rozwiązują oryginalny problem, ale chcemy zrozumieć, które z tych dwóch rozwiązań używa zamknięć , aby to osiągnąć.

Author: Azeezah M, 2012-10-17

12 answers

Uwaga wydawcy: wszystkie funkcje w JavaScript są zamknięciami, jak wyjaśniono w tym poście. Jednakże jesteśmy zainteresowani jedynie identyfikacją podzbioru tych funkcji, które są interesujące z teoretycznego punktu widzenia. Odtąd każde odniesienie do słowa zamknięcie będzie odnosiło się do tego podzbioru funkcji, chyba że zaznaczono inaczej.

Proste wyjaśnienie zamknięcia:

  1. weź funkcję. Nazwijmy to F.
  2. lista wszystkie zmienne F
  3. zmienne mogą być dwóch typów:
    1. zmienne lokalne (zmienne związane)
    2. zmienne nielokalne (Zmienne wolne)
  4. jeśli F nie ma wolnych zmiennych, to nie może być zamknięciem.
  5. jeśli F ma dowolne zmienne (które są zdefiniowane w a nadrzędnym zakresie F), to:
    1. musi być tylko jeden nadrzędny zakres F, do którego jest związana wolna zmienna .
    2. If F is referred z zewnątrz tego rodzicielskiego zakresu, wtedy staje się on zamknięciem dla tej wolnej zmiennej.
    3. że zmienna wolna nazywa się wartością upvalue zamknięcia F.

Teraz użyjmy tego, aby dowiedzieć się, kto używa zamknięć, a kto nie (dla wyjaśnienia nazwałem funkcje): {]}

Przypadek 1: Program Twojego przyjaciela

for (var i = 0; i < 10; i++) {
    (function f() {
        var i2 = i;
        setTimeout(function g() {
            console.log(i2);
        }, 1000);
    })();
}

W powyższym programie są dwie funkcje: f i g. Zobaczmy, czy są to zamknięcia:

Dla f:

  1. lista zmiennych:
    1. i2 jest zmienną lokalną .
    2. i jest zmienną wolną .
    3. setTimeout jest zmienną wolną .
    4. g jest zmiennąlokalną .
    5. console jest zmiennąwolną .
  2. Znajdź zakres nadrzędny, do którego przypisana jest każda wolna zmienna:
    1. i jest związane z globalnym zakres.
    2. setTimeout jestzwiązane z zakresem globalnym.
    3. console jest związane z zakresem globalnym.
  3. w jakim zakresie znajduje się funkcjaodwołująca się ? zasięg globalny .
    1. stąd i nie jest zamknięte przez przez f.
    2. stąd setTimeout nie jest zamknięte przez przez f.
    3. stąd console nie jest zamknięte przez przez f.

Zatem funkcja f jest nie zakończenie.

Dla g:

  1. lista zmiennych:
    1. console jest zmiennąwolną .
    2. i2 jest zmienną wolną .
  2. Znajdź zakres nadrzędny, do którego przypisana jest każda wolna zmienna:
    1. console jest związane z zakresem globalnym.
    2. i2 jest związane z zakresem f.
  3. w jakim zakresie znajduje się funkcjaodwołująca się ? zakres setTimeout.
    1. stąd console nie jest zamknięte przez przez g.
    2. stąd i2 jest zamknięte przez przez g.

Zatem funkcja g jest zamknięciem dla zmiennej swobodnej i2 (która jest wartością upvalue dla g) gdy {[75] }to odnosi się od wewnątrz setTimeout.

źle dla Ciebie: twój przyjaciel używa zamknięcia. Wewnętrzną funkcją jest zamknięcie.

Przypadek 2: Twój Program

for (var i = 0; i < 10; i++) {
    setTimeout((function f(i2) {
        return function g() {
            console.log(i2);
        };
    })(i), 1000);
}

W powyższym programie są dwie funkcje: f i g. Zobaczmy, czy są zamknięciami:

Dla f:

  1. lista zmiennych:
    1. i2 jest zmienną lokalną .
    2. g jest zmiennąlokalną .
    3. console jest zmiennąwolną .
  2. Znajdź zakres nadrzędny, do którego przypisana jest każda wolna zmienna:
    1. console jest związane z globalnym zakres.
  3. w jakim zakresie znajduje się funkcjaodwołująca się ? zasięg globalny .
    1. stąd console nie jest zamknięte przez przez f.

Zatem funkcja f nie jest zamknięciem.

Dla g:

  1. lista zmiennych:
    1. console jest zmiennąwolną .
    2. i2 jest zmienną wolną .
  2. Znajdź zakres nadrzędny, do którego każdy wolny zmienna jest związana:
    1. console jest związane z zakresem globalnym.
    2. i2 jest związane z zakresem f.
  3. w jakim zakresie znajduje się funkcjaodwołująca się ? zakres setTimeout.
    1. stąd console nie jest zamknięte przez przez g.
    2. stąd i2 jest zamknięte przez przez g.

Zatem funkcja {[5] } jest zamknięciem zmiennej swobodnej i2 (która jest upvalue dla g) gdy {[75] }to odnosi się od wewnątrz setTimeout.

dobrze dla Ciebie: używasz zamknięcia. Wewnętrzną funkcją jest zamknięcie.

Więc ty i twój przyjaciel używacie zamknięć. Przestań się kłócić. Mam nadzieję, że oczyściłem koncepcję zamknięć i jak je zidentyfikować dla was obojga.

Edit: proste wyjaśnienie, dlaczego wszystkie funkcje są zamknięte (credits @Peter):

Najpierw rozważmy następujące program (it ' s the control):

lexicalScope();

function lexicalScope() {
    var message = "This is the control. You should be able to see this message being alerted.";

    regularFunction();

    function regularFunction() {
        alert(eval("message"));
    }
}
  1. wiemy, że zarówno lexicalScope, jak i regularFunction nie są zamknięciami z powyższej definicji .
  2. kiedy wykonujemy program oczekujemy message zaalarmować , ponieważ regularFunction nie jest zamknięciem (tzn. ma dostęp do wszystkich zmiennych w swoim zakresie nadrzędnym-w tym message).
  3. kiedy wykonujemy program obserwujemy że message jest rzeczywiście / align = "left" /

Następnie rozważmy następujący program (jest to alternatywa):

var closureFunction = lexicalScope();

closureFunction();

function lexicalScope() {
    var message = "This is the alternative. If you see this message being alerted then in means that every function in JavaScript is a closure.";

    return function closureFunction() {
        alert(eval("message"));
    };
}
  1. wiemy, że tylko closureFunction jest zamknięciem z powyższej definicji .
  2. kiedy wykonujemy program oczekujemy message Nie należy zaalarmować , ponieważ closureFunction jest zamknięciem (tzn. ma dostęp tylko do wszystkich swoich zmiennych nielokalnych w momencie utworzenia funkcji (Zobacz też odpowiedź) - Nie dotyczy to message).
  3. kiedy wykonujemy program obserwujemy , że message jest faktycznie alarmowany.

Co z tego wywnioskujemy?

  1. interpretery JavaScript nie traktują zamknięć inaczej niż inne funkcje.
  2. każda funkcja niesie ze sobą swój łańcuch zakresu wraz z nim. Zamknięcia nie mają oddzielnego środowiska odniesienia.
  3. zamknięcie jest jak każda inna funkcja. Po prostu nazywamy je zamknięciami, gdy są odwołane {[75] } w zakresie poza zakresem, do którego należą , ponieważ jest to interesujący przypadek.
 621
Author: Aadit M Shah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:13

Zgodnie z definicją closure:

"zamknięcie" jest wyrażeniem (zazwyczaj funkcją), które może mieć Wolne zmienne wraz ze środowiskiem , które wiąże te zmienne (które" zamyka " wyrażenie).

Używasz closure jeśli zdefiniujesz funkcję, która używa zmiennej zdefiniowanej poza funkcją. (zmienną nazywamy zmienną wolną ).
Wszystkie używają closure (nawet w pierwszym przykładzie).

 91
Author: kev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-17 09:09:49

W skrócie zamknięcia Javascript pozwalają funkcji uzyskać dostęp do zmiennej, która jest zadeklarowana w funkcji rodzica leksykalnego.

Zobaczmy bardziej szczegółowe wyjaśnienie. Aby zrozumieć zamknięcia, ważne jest, aby zrozumieć, w jaki sposób JavaScript zakresy zmiennych.

Lunety

W JavaScript zakresy definiowane są za pomocą funkcji. Każda funkcja definiuje nowy zakres.

Rozważ następujące przykład;

function f()
{//begin of scope f
  var foo='hello'; //foo is declared in scope f
  for(var i=0;i<2;i++){//i is declared in scope f
     //the for loop is not a function, therefore we are still in scope f
     var bar = 'Am I accessible?';//bar is declared in scope f
     console.log(foo);
  }
  console.log(i);
  console.log(bar);
}//end of scope f

Wywołanie wydruków f

hello
hello
2
Am I Accessible?

Rozważmy teraz przypadek, w którym mamy funkcję g zdefiniowaną w innej funkcji f.

function f()
{//begin of scope f
  function g()
  {//being of scope g
    /*...*/
  }//end of scope g
  /*...*/
}//end of scope f

Nazwiemy f rodzicem leksykalnym Z g. Jak wyjaśniono wcześniej, mamy teraz 2 zakresy; zakres f i zakres g.

Ale jeden zakres jest "wewnątrz" drugiego zakresu, więc czy zakres funkcji potomnej jest częścią zakresu funkcji rodzica? Co dzieje się z zadeklarowanymi zmiennymi w zakresie funkcji rodzica; czy będę mógł uzyskać do nich dostęp z zakresu funkcji dziecka? Właśnie tam wkraczają zamknięcia.

Zamknięcia

W JavaScript funkcja g może nie tylko uzyskać dostęp do zmiennych zadeklarowanych w zakresie g, ale także do zmiennych zadeklarowanych w zakresie funkcji nadrzędnej f.

Rozważ podążanie za;

function f()//lexical parent function
{//begin of scope f
  var foo='hello'; //foo declared in scope f
  function g()
  {//being of scope g
    var bar='bla'; //bar declared in scope g
    console.log(foo);
  }//end of scope g
  g();
  console.log(bar);
}//end of scope f

Wywołanie wydruków f

hello
undefined

Spójrzmy na linię console.log(foo);. W tym punkt jesteśmy w scope g i staramy się uzyskać dostęp do zmiennej {[20] } zadeklarowanej w scope f. Ale jak już wspomniano, możemy uzyskać dostęp do dowolnej zmiennej zadeklarowanej w funkcji rodzica leksykalnego, co ma miejsce w tym przypadku; g jest rodzicem leksykalnym f. Dlatego hello jest drukowany.
Spójrzmy teraz na linię console.log(bar);. W tym momencie znajdujemy się w scope f i staramy się uzyskać dostęp do zmiennej bar zadeklarowanej w scope g. {[27] } nie jest zadeklarowana w bieżącym zakresie i funkcja g nie jest rodzicem f, dlatego {[27] } jest niezdefiniowany

W rzeczywistości możemy również uzyskać dostęp do zmiennych zadeklarowanych w zakresie leksykalnej funkcji "grand parent". Jeśli więc istnieje funkcja h zdefiniowana w funkcji g

function f()
{//begin of scope f
  function g()
  {//being of scope g
    function h()
    {//being of scope h
      /*...*/
    }//end of scope h
    /*...*/
  }//end of scope g
  /*...*/
}//end of scope f

Wtedy {[33] } będzie w stanie uzyskać dostęp do wszystkich zmiennych zadeklarowanych w zakresie funkcji h, g, i f. Odbywa się to za pomocą zamknięć . W JavaScript closures pozwala nam na dostęp każda zmienna zadeklarowana w leksykalnej funkcji rodzica, w leksykalnej funkcji rodzica Wielkiego, w leksykalnej funkcji rodzica wielkiego-wielkiego itd. To może być postrzegane jako łańcuch zakresu; scope of current function -> scope of lexical parent function -> scope of lexical grand parent function -> ... aż do ostatniej funkcji rodzica, która nie ma rodzica leksykalnego.

Obiekt window

W rzeczywistości łańcuch nie zatrzymuje się na ostatniej funkcji rodzica. Jest jeszcze jeden specjalny zakres; globalny zakres . Każda zmienna nie zadeklarowana w funkcji jest uważana za deklarowane w skali globalnej. Globalny zasięg ma dwie specjalności;

  • każda zmienna zadeklarowana w globalnym zasięgu jest dostępna wszędzie
  • zmienne zadeklarowane w globalnym zakresie odpowiadają właściwościom obiektu window.

Dlatego istnieją dokładnie dwa sposoby deklarowania zmiennej foo w globalnym zasięgu; albo przez nie deklarowanie jej w funkcji, albo przez ustawienie właściwości foo okna obiekt.

Obie próby wykorzystują zamknięcia

Teraz, gdy przeczytałeś bardziej szczegółowe wyjaśnienie, może teraz być oczywiste, że oba rozwiązania wykorzystują zamknięcia. Ale dla pewności, zróbmy dowód.

Stwórzmy nowy język programowania; JavaScript-No-Closure. Jak sama nazwa wskazuje, JavaScript-No-Closure jest identyczny z JavaScript, z wyjątkiem tego, że nie obsługuje zamykania.

Innymi słowy;

var foo = 'hello';
function f(){console.log(foo)};
f();
//JavaScript-No-Closure prints undefined
//JavaSript prints hello

W porządku, zobaczmy, co się stanie z pierwszym rozwiązanie z JavaScript-No-Closure;

for(var i = 0; i < 10; i++) {
  (function(){
    var i2 = i;
    setTimeout(function(){
        console.log(i2); //i2 is undefined in JavaScript-No-Closure 
    }, 1000)
  })();
}

Dlatego będzie to drukować undefined 10 razy w JavaScript-No-Closure.

Stąd pierwsze rozwiązanie używa zamknięcia.

Spójrzmy na drugie rozwiązanie;

for(var i = 0; i < 10; i++) {
  setTimeout((function(i2){
    return function() {
        console.log(i2); //i2 is undefined in JavaScript-No-Closure
    }
  })(i), 1000);
}

Dlatego będzie to drukować undefined 10 razy w JavaScript-No-Closure.

Oba rozwiązania wykorzystują zamknięcia.

Edit: zakłada się, że te 3 fragmenty kodu nie są zdefiniowane w globalnym zasięgu. Inaczej zmienne foo i i jest on związany z obiektem window i dlatego jest dostępny przez obiekt window zarówno w JavaScript, jak i JavaScript-No-Closure.

 46
Author: brillout.com,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-24 18:02:54

Nigdy nie byłem zadowolony ze sposobu, w jaki ktoś to wyjaśnia.

Kluczem do zrozumienia zamknięć jest zrozumienie, jak wyglądałby JS bez zamknięć.

Bez zamknięcia, to rzuci błąd

function outerFunc(){
    var outerVar = 'an outerFunc var';
    return function(){
        alert(outerVar);
    }
}

outerFunc()(); //returns inner function and fires it

Gdy outerFunc powróci w wyimaginowanej, wyłączonej wersji JavaScript, odniesienie do outervara będzie zbierane i zniknie, nie pozostawiając nic do odwołania do wewnętrznego func.

Zamknięcia są zasadniczo specjalne reguły, które wprowadzają i umożliwiają istnienie tych zmiennych, gdy funkcja wewnętrzna odwołuje się do zmiennych funkcji zewnętrznej. W przypadku zamknięć odwołane var są utrzymywane nawet po zakończeniu zewnętrznej funkcji lub "zamknięciu", jeśli to pomaga zapamiętać punkt.

Nawet w przypadku zamknięć, cykl życia lokalnych VAR-ów w funkcji bez wewnętrznych funkcji odwołujących się do lokalnych funkcji działa tak samo, jak w wersji bez zamknięcia. Po zakończeniu funkcji mieszkańcy otrzymują śmieci zebrane.

Gdy masz odniesienie w wewnętrznym func do zewnętrznego var, jednak to tak, jakby klamka drzwi została umieszczona na drodze do zbierania śmieci dla tych odwołanych var.

Być może dokładniejszym sposobem spojrzenia na zamknięcia jest to, że wewnętrzna funkcja zasadniczo używa wewnętrznego zakresu jako własnego zakresu.

Ale przywołany kontekst jest w rzeczywistości trwały, a nie jak migawka. wielokrotne odpalanie zwracanej funkcji wewnętrznej, która ciągle zwiększa i rejestrowanie lokalnego var funkcji zewnętrznej będzie nadal alarmować wyższe wartości.

function outerFunc(){
    var incrementMe = 0;
    return function(){ incrementMe++; console.log(incrementMe); }
}
var inc = outerFunc();
inc(); //logs 1
inc(); //logs 2
 19
Author: Erik Reppen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-06 15:29:52

Oboje używacie zamknięć.

Wybieram definicję Wikipedii tutaj:

W informatyce zamknięcie (także zamknięcie leksykalne lub funkcja zamknięcie) jest funkcją lub odniesieniem do funkcji wraz z środowisko odniesienia-tabela zawierająca odniesienie do każdego z zmienne nielokalne (zwane też zmiennymi wolnymi) tej funkcji. Zamknięcie-w przeciwieństwie do zwykłego wskaźnika funkcji-umożliwia funkcji Dostęp te zmienne nielokalne nawet w przypadku wywołania poza jego bezpośrednim zakres leksykalny.

Próba znajomego wyraźnie używa zmiennej i, która nie jest lokalna, pobierając jej wartość i wykonując kopię do przechowywania w lokalnym i2.

Twoja własna próba przekazuje i (która na stronie wywołania jest w zakresie) do funkcji anonimowej jako argumentu. Na razie nie jest to zamknięcie, ale wtedy funkcja zwraca inną funkcję, która odwołuje się do tego samego i2. Ponieważ wewnątrz wewnętrznej funkcji anonimowej i2 nie jest lokalny, to tworzy zamknięcie.

 16
Author: Jon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-17 09:08:52

Ty i twój przyjaciel używacie zamknięć:

Zamknięcie jest szczególnym rodzajem obiektu, który łączy dwie rzeczy: funkcję i środowisko, w którym ta funkcja została stworzona. Środowisko składa się z dowolnych zmiennych lokalnych, które były w zasięgu w momencie utworzenia zamknięcia.

MDN: https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Closures

W funkcji kodu Twojego znajomego function(){ console.log(i2); } zdefiniowanej wewnątrz funkcja anonimowa function(){ var i2 = i; ... i potrafi odczytywać/zapisywać zmienną lokalną i2.

W funkcji kodu function(){ console.log(i2); } zdefiniowanej wewnątrz funkcji function(i2){ return ... i potrafi odczytywać / zapisywać lokalne wartości i2 (deklarowany w tym przypadku jako parametr).

W obu przypadkach funkcja function(){ console.log(i2); } przechodzi następnie do setTimeout.

Innym odpowiednikiem (ale z mniejszym wykorzystaniem pamięci) jest:

function fGenerator(i2){
    return function(){
        console.log(i2);
    }
}
for(var i = 0; i < 10; i++) {
    setTimeout(fGenerator(i), 1000);
}
 12
Author: Andrew D.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-24 16:51:51

Zamknięcie

Zamknięcie nie jest funkcją, a nie wyrażeniem. Musi być postrzegany jako rodzaj "migawki" z użytych zmiennych spoza zakresu funkcji i używanych wewnątrz funkcji. Gramatycznie należy powiedzieć: "weź zamknięcie zmiennych".

Innymi słowy: zamknięcie jest kopią odpowiedniego kontekstu zmiennych, od którego zależy funkcja.

Jeszcze raz (naïf): Zamknięcie to dostęp do zmiennych, które nie są przekazywane jako parametr.

Należy pamiętać, że te pojęcia funkcjonalne silnie zależą od używanego języka / środowiska programowania. W JavaScript zamknięcie zależy od zakresu leksykalnego (co jest prawdziwe w większości języków c).

Zatem zwracanie funkcji jest najczęściej zwracaniem funkcji anonimowej / nienazwanej. Gdy funkcja uzyskuje dostęp do zmiennych, nie przekazywanych jako parametr, i w jej (leksykalnym) zakresie, następuje zamknięcie.

Więc, jeśli chodzi o twoje przykłady:

// 1
for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i); // closure, only when loop finishes within 1000 ms,
    }, 1000);           // i = 10 for all functions
}
// 2
for(var i = 0; i < 10; i++) {
    (function(){
        var i2 = i; // closure of i (lexical scope: for-loop)
        setTimeout(function(){
            console.log(i2); // closure of i2 (lexical scope:outer function)
        }, 1000)
    })();
}
// 3
for(var i = 0; i < 10; i++) {
    setTimeout((function(i2){
        return function() {
            console.log(i2); // closure of i2 (outer scope)

        }
    })(i), 1000); // param access i (no closure)
}

Wszystkie używają zamknięć. Nie myl miejsca egzekucji z zamknięciami. Jeśli "migawka" zamknięcia zostanie wykonana w niewłaściwym momencie, wartości mogą być nieoczekiwane, ale z pewnością zostanie podjęte zamknięcie!

 9
Author: Andries,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-17 10:35:32

Spójrzmy na obie strony:

(function(){
    var i2 = i;
    setTimeout(function(){
        console.log(i2);
    }, 1000)
})();

Deklaruje i natychmiast wykonuje anonimową funkcję, która działa setTimeout() we własnym kontekście. Aktualna wartość i jest zachowywana przez wykonanie kopii do i2 jako pierwsza; działa ze względu na natychmiastowe wykonanie.

setTimeout((function(i2){
    return function() {
        console.log(i2);
    }
})(i), 1000);

Deklaruje kontekst wykonania dla wewnętrznej funkcji, w którym bieżąca wartość i jest zachowana do i2; to podejście wykorzystuje również natychmiastowe wykonanie do zachowania wartość.

Ważne

Należy wspomnieć, że semantyka run nie jest taka sama między obydwoma podejściami; twoja wewnętrzna funkcja jest przekazywana do setTimeout(), podczas gdy jego wewnętrzna funkcja wywołuje setTimeout() siebie.

Owinięcie obu kodów wewnątrz innego {[2] }nie dowodzi, że tylko drugie podejście używa zamknięć, po prostu nie ma tego samego na początku.

Wniosek

Obie metody używają zamknięć, więc sprowadza się to do osobistego smak; drugie podejście jest łatwiejsze do" poruszania się " lub uogólniania.

 9
Author: Ja͢ck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-17 11:28:24

Napisałem to jakiś czas temu, aby przypomnieć sobie, co to jest zamknięcie i jak działa w JS.

Zamknięcie jest funkcją, która po wywołaniu używa zakresu, w którym została zadeklarowana, a nie zakresu, w którym została wywołana. W javaScript wszystkie funkcje zachowują się tak. Wartości zmiennych w zakresie utrzymują się tak długo, jak długo istnieje funkcja, która nadal do nich wskazuje. Wyjątkiem od reguły jest 'this', która odnosi się do obiektu, w którym znajduje się funkcja, gdy jest wywoływana.

var z = 1;
function x(){
    var z = 2; 
    y(function(){
      alert(z);
    });
}
function y(f){
    var z = 3;
    f();
}
x(); //alerts '2' 
 7
Author: Nat Darke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-21 19:10:29

Po dokładnym sprawdzeniu wygląda na to, że oboje używacie zamknięcia.

W przypadku twoich znajomych, {[0] } jest dostępna wewnątrz funkcji anonimowej 1, A {[1] } jest dostępna w funkcji anonimowej 2, Gdzie console.log jest obecna.

W Twoim przypadku masz dostęp do i2 wewnątrz funkcji anonimowej, gdzie console.log jest obecna. Dodaj instrukcję debugger; przed console.log i w narzędziach programistycznych chrome pod "zmienne zakresu" powie, w jakim zakresie jest zmienna.

 5
Author: Ramesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-17 09:07:59

Rozważ, co następuje. Tworzy to i odtwarza funkcję f, która zamyka się na i, ale inną!:

i=100;

f=function(i){return function(){return ++i}}(0);
alert([f,f(),f(),f(),f(),f(),f(),f(),f(),f(),f()].join('\n\n'));

f=function(i){return new Function('return ++i')}(0);        /*  function declarations ~= expressions! */
alert([f,f(),f(),f(),f(),f(),f(),f(),f(),f(),f()].join('\n\n'));

Podczas gdy następująca funkcja "a" zamyka się "sama"
(sami! po tym fragmencie używa się pojedynczego referenta f )

for(var i = 0; i < 10; i++) {
    setTimeout( new Function('console.log('+i+')'),  1000 );
}

Lub bardziej wprost:

for(var i = 0; i < 10; i++) {
    console.log(    f = new Function( 'console.log('+i+')' )    );
    setTimeout( f,  1000 );
}

NB. ostatnia definicja f to function(){ console.log(9) } przed 0 jest drukowana.

Uwaga! Koncepcja zamknięcia może być przymusem odwracania uwagi od istoty programowania elementarnego:

for(var i = 0; i < 10; i++) {     setTimeout( 'console.log('+i+')',  1000 );      }

X-refs.:
Jak działają zamknięcia JavaScript?
Javascript Closures Explanation
czy zamknięcie (JS) wymaga funkcji wewnątrz funkcji
Jak rozumieć zamknięcia w Javascript?
JavaScript local and global variable confusion

 3
Author: ekim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:07

Chciałbym podzielić się moim przykładem i wyjaśnieniem na temat zamknięć. Zrobiłem przykład Pythona i dwie liczby, aby zademonstrować Stany stosu.

def maker(a, b, n):
    margin_top = 2
    padding = 4
    def message(msg):
        print('\n’ * margin_top, a * n, 
            ' ‘ * padding, msg, ' ‘ * padding, b * n)
    return message

f = maker('*', '#', 5)
g = maker('', '♥’, 3)
…
f('hello')
g(‘good bye!')

Wynik tego kodu będzie następujący:

*****      hello      #####

      good bye!    ♥♥♥

Oto dwie figury pokazujące stosy i zamknięcie dołączone do obiektu funkcji.

Gdy funkcja jest zwracana z Makera

Gdy funkcja zostanie wywołana później

Gdy funkcja jest wywoływana przez parametr lub zmienna nielokalna, kod wymaga lokalnych powiązań zmiennych, takich jak margin_top, padding, a także a, b, n. w celu zapewnienia działania kodu funkcji, ramka stosu funkcji maker, która odeszła dawno temu, powinna być dostępna, co jest wspierane w zamknięciu, które możemy znaleźć wraz z obiektem wiadomości function.

 0
Author: Eunjung Lee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-12 03:45:42