Jak wyjaśniłbyś zamknięcia JavaScript komuś, kto zna pojęcia, na które składają się (na przykład funkcje, zmienne i tym podobne), ale nie rozumie samych zamknięć?
Widziałem przykład programu podany na Wikipedii, ale niestety nie pomógł.
Jak wyjaśniłbyś zamknięcia JavaScript komuś, kto zna pojęcia, na które składają się (na przykład funkcje, zmienne i tym podobne), ale nie rozumie samych zamknięć?
Widziałem przykład programu podany na Wikipedii, ale niestety nie pomógł.
closure
kodu, w przeciwieństwie do tego,Object Literal
który ponownie wykorzystuje się sam i zmniejsza to samo obciążenie, ale wymaga o 100% mniej kodu owijania.Odpowiedzi:
Zamknięcie to połączenie:
Środowisko leksykalne jest częścią każdego kontekstu wykonania (ramki stosu) i stanowi mapę między identyfikatorami (tj. Lokalnymi nazwami zmiennych) a wartościami.
Każda funkcja w JavaScript utrzymuje odniesienie do zewnętrznego środowiska leksykalnego. Odwołanie to służy do konfigurowania kontekstu wykonania utworzonego po wywołaniu funkcji. To odwołanie umożliwia kodowi wewnątrz funkcji „zobaczenie” zmiennych zadeklarowanych poza funkcją, niezależnie od tego, kiedy i gdzie funkcja jest wywoływana.
Jeśli funkcja została wywołana przez funkcję, która z kolei została wywołana przez inną funkcję, powstaje łańcuch odniesień do zewnętrznych środowisk leksykalnych. Ten łańcuch nazywa się łańcuchem zakresu.
W poniższym kodzie
inner
tworzy zamknięcie ze środowiskiem leksykalnym kontekstu wykonania utworzonego pofoo
wywołaniu, zamykając zmiennąsecret
:Innymi słowy: w JavaScript funkcje zawierają odniesienie do prywatnego „pola stanu”, do którego mają dostęp tylko one (i wszelkie inne funkcje zadeklarowane w tym samym środowisku leksykalnym). To pole stanu jest niewidoczne dla osoby wywołującej funkcję, zapewniając doskonały mechanizm ukrywania danych i enkapsulacji.
I pamiętaj: funkcje w JavaScript mogą być przekazywane jak zmienne (funkcje pierwszej klasy), co oznacza, że te pary funkcjonalności i stanu mogą być przekazywane wokół twojego programu: podobnie jak możesz przekazać instancję klasy w C ++.
Gdyby JavaScript nie miał zamknięć, wówczas więcej funkcji musiałoby zostać jawnie przekazanych między funkcjami , dzięki czemu listy parametrów byłyby dłuższe, a kod zakłócany.
Jeśli więc chcesz, aby funkcja zawsze miała dostęp do prywatnego stanu, możesz użyć zamknięcia.
... i często my nie chcemy terytorium stowarzyszone z funkcji. Na przykład w Javie lub C ++, kiedy dodajesz zmienną instancji prywatnej i metodę do klasy, kojarzysz stan z funkcjonalnością.
W C i większości innych popularnych języków po zwróceniu funkcji wszystkie zmienne lokalne nie są już dostępne, ponieważ rama stosu jest zniszczona. W JavaScript, jeśli zadeklarujesz funkcję w innej funkcji, wówczas lokalne zmienne funkcji zewnętrznej mogą pozostać dostępne po powrocie z niej. W ten sposób, w powyższym kodzie
secret
pozostaje dostępny do obiektu funkcyjnegoinner
, po to został zwrócony zfoo
.Zastosowania zamknięć
Zamknięcia są przydatne, gdy potrzebujesz prywatnego stanu powiązanego z funkcją. Jest to bardzo częsty scenariusz - i pamiętaj: JavaScript nie miał składni klas do 2015 r. I nadal nie ma składni pól prywatnych. Zamknięcia spełniają tę potrzebę.
Zmienne instancji prywatnej
W poniższym kodzie funkcja
toString
zamyka szczegóły samochodu.Programowanie funkcjonalne
W poniższym kodzie funkcja
inner
zamyka się zarówno na, jakfn
i naargs
.Programowanie zorientowane na zdarzenia
W poniższym kodzie funkcja
onClick
zamyka się nad zmiennąBACKGROUND_COLOR
.Modularyzacja
W poniższym przykładzie wszystkie szczegóły implementacji są ukryte w natychmiast wykonywanym wyrażeniu funkcji. Funkcje
tick
i funkcje związanetoString
z państwem prywatnym oraz funkcje potrzebne do ukończenia pracy. Zamknięcia umożliwiły nam modularyzację i enkapsulację naszego kodu.Przykłady
Przykład 1
Ten przykład pokazuje, że zmienne lokalne nie są kopiowane w zamknięciu: zamknięcie zachowuje odwołanie do samych zmiennych pierwotnych . To tak, jakby ramka stosu pozostała przy życiu w pamięci nawet po wyjściu z funkcji zewnętrznej.
Przykład 2
W poniższym kodzie, trzech metod
log
,increment
aupdate
wszystko Zamknij nad samym środowisku leksykalnym.I przy każdym
createObject
wywołaniu tworzony jest nowy kontekst wykonania (ramka stosu) i tworzona jest zupełnie nowa zmiennax
oraz nowy zestaw funkcji (log
itp.), Które zamykają tę nową zmienną.Przykład 3
Jeśli używasz zmiennych zadeklarowanych za pomocą
var
, uważaj, aby zrozumieć, którą zmienną zamykasz. Zmienne zadeklarowane za pomocąvar
są podnoszone. Jest to o wiele mniejszy problem we współczesnym JavaScript ze względu na wprowadzenielet
iconst
.W poniższym kodzie za każdym razem wokół pętli
inner
tworzona jest nowa funkcja , która się zamykai
. Ponieważ jednakvar i
jest podnoszony poza pętlę, wszystkie te funkcje wewnętrzne zamykają się w tej samej zmiennej, co oznacza, żei
trzykrotnie drukowana jest końcowa wartość (3).Punkty końcowe:
function
z wewnątrz innej funkcji jest klasycznym przykładem zamknięcia, ponieważ stan wewnątrz funkcji zewnętrznej jest domyślnie dostępny dla zwróconej funkcji wewnętrznej, nawet po zakończeniu wykonywania funkcji zewnętrznej.eval()
wewnątrz funkcji, używane jest zamknięcie. Tekst, do któregoeval
możesz odwoływać się do zmiennych lokalnych funkcji, aw trybie nieściągalnym możesz nawet tworzyć nowe zmienne lokalne za pomocąeval('var foo = …')
.new Function(…)
( konstruktora funkcji ) wewnątrz funkcji, nie zamyka się ona w swoim środowisku leksykalnym: zamyka się w kontekście globalnym. Nowa funkcja nie może odwoływać się do zmiennych lokalnych funkcji zewnętrznej.Spinki do mankietów
źródło
Closures are functions that refer to independent (free) variables. In other words, the function defined in the closure 'remembers' the environment in which it was created.
from developer.mozilla.org/en-US/docs/Web/JavaScript/Closureslet i = 0
zamiastvar i = 0
w przykładzie 5, totestList()
wydrukuje to, co chcesz oryginalnie.Każda funkcja w JavaScript utrzymuje link do zewnętrznego środowiska leksykalnego. Środowisko leksykalne to mapa wszystkich nazw (np. Zmiennych, parametrów) w zakresie wraz z ich wartościami.
Tak więc, ilekroć zobaczysz
function
słowo kluczowe, kod wewnątrz tej funkcji ma dostęp do zmiennych zadeklarowanych poza funkcją.To się zaloguje,
16
ponieważ funkcjabar
zamyka parametrx
i zmiennątmp
, które istnieją w środowisku leksykalnym funkcji zewnętrznejfoo
.Funkcja
bar
wraz z jej powiązaniem ze środowiskiem leksykalnym funkcjifoo
jest zakończeniem.Funkcja nie musi zwracać się , aby utworzyć zamknięcie. Po prostu ze względu na swoją deklarację każda funkcja zamyka się w otaczającym ją środowisku leksykalnym, tworząc zamknięcie.
Powyższa funkcja rejestruje również 16, ponieważ kod wewnątrz
bar
może nadal odwoływać się do argumentówx
i zmiennychtmp
, nawet jeśli nie są one już bezpośrednio objęte zakresem.Ponieważ jednak
tmp
nadal kręci się wewnątrzbar
zamknięcia, można go zwiększać. Będzie zwiększany za każdym razem, gdy zadzwoniszbar
.Najprostszym przykładem zamknięcia jest:
Po wywołaniu funkcji JavaScript
ec
tworzony jest nowy kontekst wykonania . Wraz z argumentami funkcji i obiektem docelowym ten kontekst wykonania otrzymuje również łącze do środowiska leksykalnego wywołującego kontekstu wykonania, co oznacza, że zmienne zadeklarowane w zewnętrznym środowisku leksykalnym (w powyższym przykładzie zarównoa
ib
) są dostępne zec
.Każda funkcja tworzy zamknięcie, ponieważ każda funkcja ma link do zewnętrznego środowiska leksykalnego.
Zauważ, że same zmienne są widoczne z wnętrza zamknięcia, a nie z kopii.
źródło
delete
zawodzi. Niemniej jednak środowisko leksykalne, które funkcja będzie nosić jako [[Zakres]] (i ostatecznie użyje jako podstawy dla własnego środowiska leksykalnego po wywołaniu) jest określane, gdy zostanie wykonana instrukcja definiująca funkcję. Oznacza to, że funkcja jest zamykanie nad całą zawartością w zakresie wykonania, niezależnie od tego, która wartość jest w rzeczywistości odnosi się do tego, czy ucieka zakresu. Proszę spojrzeć na sekcje 13.2 i 10 w specyfikacjiPRZEDMOWA: odpowiedź została napisana, gdy pytanie brzmiało:
Jestem pewien, że byłem jedną z niewielu osób, które próbowały dosłownie odpowiedzieć na pierwsze pytanie. Od tego czasu pytanie kilkakrotnie mutowało, więc moja odpowiedź może teraz wydawać się niesamowicie głupia i nie na miejscu. Mamy nadzieję, że ogólny pomysł tej historii jest dla niektórych zabawny.
Jestem wielkim fanem analogii i metafory podczas wyjaśniania trudnych pojęć, więc pozwól mi spróbować swoich sił z historią.
Pewnego razu:
Była księżniczka ...
Mieszkała w cudownym świecie pełnym przygód. Poznała swojego księcia z bajki, jeździła po świecie jednorożcem, walczyła ze smokami, napotykała gadające zwierzęta i wiele innych fantastycznych rzeczy.
Ale zawsze musiała wracać do nudnego świata obowiązków i dorosłych.
I często opowiadała im o swojej ostatniej niesamowitej przygodzie jako księżniczki.
Ale wszystko, co zobaczą, to mała dziewczynka ...
... opowiadanie historii o magii i fantastyce.
I chociaż dorośli wiedzieli o prawdziwych księżniczkach, nigdy nie uwierzyliby w jednorożce lub smoki, ponieważ nigdy ich nie zobaczą. Dorośli powiedzieli, że istnieją tylko w wyobraźni małej dziewczynki.
Ale znamy prawdziwą prawdę; że dziewczynka z księżniczką w środku ...
... to naprawdę księżniczka z małą dziewczynką w środku.
źródło
story()
funkcji, która jest jedynym interfejsem, jakilittleGirl
instancja udostępnia w świecie magii.story
zamknięcie, ale gdyby kod był,var story = function() {}; return story;
tolittleGirl
byłoby zamknięcie. Przynajmniej takie wrażenie wywarło na mnie zastosowanie przez MDN „prywatnych” metod z zamknięciami : „Te trzy funkcje publiczne to zamknięcia, które dzielą to samo środowisko”.story
jest zamknięciem odnoszącym się do środowiska przewidzianego w zakresieprincess
.princess
jest także innym domniemanym zamknięciem, tj.princess
ilittleGirl
podzieliłyby wszelkie odniesienia doparents
tablicy, która istniałaby z powrotem w środowisku / zakresie, w którymlittleGirl
istnieje iprincess
jest zdefiniowana.princess
niż to, co napisano. Niestety, ta historia jest teraz trochę nie na miejscu w tym wątku. Pierwotnie pytanie dotyczyło „wyjaśnienia zamknięć JavaScript dla 5-latka”; moja odpowiedź była jedyną, która nawet próbowała to zrobić. Nie wątpię, że zawiodłoby to nieszczęśliwie, ale przynajmniej ta odpowiedź mogła mieć szansę na zainteresowanie 5-latka.Biorąc to pytanie na poważnie, powinniśmy dowiedzieć się, co typowy 6-latek potrafi poznawczo, choć wprawdzie osoba zainteresowana JavaScriptem nie jest taka typowa.
O rozwoju dzieciństwa: od 5 do 7 lat mówi:
Możemy użyć tego przykładu, aby wyjaśnić zamknięcia w następujący sposób:
Możemy to zakodować w JavaScript w następujący sposób:
Dalsze punkty wyjaśniające, dlaczego zamknięcia są interesujące:
makeKitchen()
jest wywoływane, tworzone jest nowe zamknięcie z osobnymtrashBags
.trashBags
Zmienna jest lokalny do wnętrza każdej kuchni i nie jest dostępny na zewnątrz, ale wewnętrzna funkcja nagetTrashBag
własność ma do niego dostępu.getTrashBag
funkcji.źródło
The Straw Man
Muszę wiedzieć, ile razy kliknięto przycisk i robić co trzecie kliknięcie ...
Dość oczywiste rozwiązanie
Teraz to zadziała, ale wkracza w zakres zewnętrzny, dodając zmienną, której jedynym celem jest śledzenie liczby. W niektórych sytuacjach byłoby to preferowane, ponieważ zewnętrzna aplikacja może potrzebować dostępu do tych informacji. Ale w tym przypadku zmieniamy zachowanie tylko co trzecie kliknięcie, dlatego lepiej jest zawrzeć tę funkcjonalność w module obsługi zdarzeń .
Rozważ tę opcję
Zwróć uwagę na kilka rzeczy tutaj.
W powyższym przykładzie używam zachowania JavaScript w zamykaniu. To zachowanie umożliwia dowolnej funkcji dostęp do zakresu, w którym została utworzona, w nieskończoność. Aby praktycznie to zastosować, natychmiast wywołuję funkcję, która zwraca inną funkcję, a ponieważ funkcja, którą zwracam, ma dostęp do wewnętrznej zmiennej zliczania (z powodu wyjaśnionego powyżej zachowania dotyczącego zamykania), skutkuje to zakresem prywatnym do użycia przez wynikowy funkcja ... Nie takie proste? Rozcieńczmy to ...
Proste zamknięcie w jednej linii
Wszystkie zmienne poza zwracaną funkcją są dostępne dla zwróconej funkcji, ale nie są bezpośrednio dostępne dla zwróconego obiektu funkcji ...
Zdobyć? Tak więc w naszym podstawowym przykładzie zmienna count jest zawarta w zamknięciu i zawsze dostępna dla procedury obsługi zdarzeń, więc zachowuje swój stan od kliknięcia do kliknięcia.
Ponadto, ten prywatny stan zmiennych jest w pełni dostępny, zarówno dla odczytów, jak i przypisywania do prywatnych zmiennych o zasięgu.
Proszę bardzo; teraz całkowicie ujmujesz to zachowanie.
Pełny post na blogu (w tym uwagi dotyczące jQuery)
źródło
Zamknięcia są trudne do wyjaśnienia, ponieważ są wykorzystywane do działania pewnych zachowań, których i tak wszyscy intuicyjnie oczekują. Znajduję najlepszy sposób na ich wyjaśnienie (i sposób, w jaki dowiedziałem się, co robią) to wyobrażenie sobie sytuacji bez nich:
Co by się tu stało, gdyby JavaScript nie wiedział o zamknięciach? Po prostu zamień wywołanie w ostatnim wierszu na treść metody (czyli w zasadzie to, co robią wywołania funkcji), a otrzymasz:
Gdzie jest definicja
x
? Nie zdefiniowaliśmy tego w bieżącym zakresie. Jedynym rozwiązaniem jestplus5
przeniesienie jego zakresu (a raczej zakresu jego rodzica). W ten sposóbx
jest dobrze zdefiniowane i powiązane z wartością 5.źródło
OK, 6-letni wentylator do zamykania. Czy chcesz usłyszeć najprostszy przykład zamknięcia?
Wyobraźmy sobie następną sytuację: kierowca siedzi w samochodzie. Ten samochód jest w samolocie. Samolot jest na lotnisku. Zdolność kierowcy do uzyskania dostępu do rzeczy poza samochodem, ale w samolocie, nawet jeśli samolot ten opuszcza lotnisko, jest zamknięciem. Otóż to. Kiedy skończysz 27 lat, spójrz na bardziej szczegółowe wyjaśnienie lub na poniższy przykład.
Oto jak mogę przekonwertować historię mojego samolotu na kod.
źródło
TLDR
Zamknięcie jest łącznikiem między funkcją a jej zewnętrznym środowiskiem leksykalnym (tj. W formie pisemnej), dzięki czemu identyfikatory (zmienne, parametry, deklaracje funkcji itp.) Zdefiniowane w tym środowisku są widoczne z wnętrza funkcji, niezależnie od tego, kiedy lub z gdzie wywoływana jest funkcja.
Detale
W terminologii specyfikacji ECMAScript można powiedzieć, że zamknięcie jest realizowane przez
[[Environment]]
odwołanie do każdego obiektu funkcji, co wskazuje na środowisko leksykalne, w którym funkcja jest zdefiniowana.Gdy funkcja jest wywoływana za pomocą
[[Call]]
metody wewnętrznej ,[[Environment]]
odwołanie do obiektu funkcji jest kopiowane do zewnętrznego odwołania do środowiska rekordu środowiska nowo utworzonego kontekstu wykonania (ramka stosu).W poniższym przykładzie funkcja
f
zamyka się w środowisku leksykalnym globalnego kontekstu wykonania:W poniższym przykładzie funkcja
h
zamyka środowisko leksykalne funkcjig
, które z kolei zamyka środowisko leksykalne globalnego kontekstu wykonania.Jeśli funkcja wewnętrzna zostanie zwrócona przez funkcję zewnętrzną, wówczas zewnętrzne środowisko leksykalne pozostanie po powrocie funkcji zewnętrznej. Wynika to z faktu, że zewnętrzne środowisko leksykalne musi być dostępne, jeśli funkcja wewnętrzna zostanie ostatecznie wywołana.
W poniższym przykładzie funkcja
j
zamyka się w środowisku leksykalnym funkcjii
, co oznacza, że zmiennax
jest widoczna z wnętrza funkcjij
, długo poi
zakończeniu wykonywania funkcji:W zamknięciu, zmienne w zewnętrznym środowisku leksykalnym sami są dostępne, nie kopie.
Łańcuch środowisk leksykalnych, połączony między kontekstami wykonywania za pomocą zewnętrznych odniesień do środowiska, tworzy łańcuch zasięgu i definiuje identyfikatory widoczne z dowolnej funkcji.
Należy pamiętać, że próbując poprawić jasność i dokładność, odpowiedź została znacznie zmieniona w stosunku do oryginału.
źródło
console.log
tym celu podstawień łańcuchów . Jeśli ktoś jest zainteresowany, jest ich więcej: developer.mozilla.org/en-US/docs/DOM/…var
).Jest to próba wyjaśnienia kilku (możliwych) nieporozumień na temat zamknięć pojawiających się w niektórych innych odpowiedziach.
źródło
Niedawno napisałem post na blogu wyjaśniający zamknięcia. Oto, co powiedziałem o zamknięciach pod względem tego, dlaczego chcesz je mieć.
W tym sensie pozwalają funkcjom zachowywać się trochę jak obiekty z prywatnymi atrybutami.
Pełny post:
Czym więc są te zamknięcia?
źródło
devError = emailError("[email protected]", errorString)
a następnie mam własną niestandardową wersję współdzielonej funkcji emailError?Zamknięcia są proste:
Poniższy prosty przykład obejmuje wszystkie główne punkty zamknięć JavaScript. *
Oto fabryka produkująca kalkulatory, które mogą dodawać i rozmnażać:
Kluczowy punkt: każde wezwanie do
make_calculator
utworzenia nowej zmiennej lokalnejn
, która będzie nadal użyteczna dla tego kalkulatoraadd
imultiply
działa długo pomake_calculator
powrocie.Jeśli jesteś zaznajomiony z ramkami stosu, te kalkulatory wydają się dziwne: Jak mogą uzyskać dostęp
n
pomake_calculator
powrocie? Odpowiedzią jest wyobrazić sobie, że JavaScript nie używa „ramek stosu”, ale zamiast tego używa „ramek sterty”, które mogą pozostać po wywołaniu funkcji, która je zwróciła.Funkcje wewnętrzne, takie jak
add
imultiply
, które mają zmienne dostępowe zadeklarowane w funkcji zewnętrznej ** , nazywane są zamknięciami .To prawie wszystko, co jest do zamknięcia.
* Na przykład, obejmuje wszystkie punkty w artykule „Zamknięcia dla manekinów” podanym w innej odpowiedzi , z wyjątkiem przykładu 6, który po prostu pokazuje, że zmiennych można użyć przed ich zadeklarowaniem, co jest miłym faktem, ale całkowicie niezwiązanym z zamknięciami. Obejmuje również wszystkie punkty w zaakceptowanej odpowiedzi , z wyjątkiem punktów (1), które funkcje kopiują swoje argumenty do zmiennych lokalnych (nazwane argumenty funkcji), oraz (2), że kopiowanie liczb tworzy nowy numer, ale kopiuje odwołanie do obiektu daje kolejne odniesienie do tego samego obiektu. Są to również dobrze wiedzieć, ale znowu całkowicie niezwiązane z zamknięciami. Jest również bardzo podobny do przykładu w tej odpowiedzi, ale nieco krótszy i mniej abstrakcyjny. Nie obejmuje to punktuta odpowiedź lub komentarz , ponieważ JavaScript utrudnia podłączenie prąduwartość zmiennej pętli do funkcji wewnętrznej: Krok „podłączenie” można wykonać tylko przy pomocy funkcji pomocnika, która obejmuje twoją funkcję wewnętrzną i jest wywoływana przy każdej iteracji pętli. (Ściśle mówiąc, funkcja wewnętrzna uzyskuje dostęp do kopii zmiennej funkcji pomocnika, zamiast podłączania czegokolwiek.) Ponownie, bardzo przydatne podczas tworzenia zamknięć, ale nie jest częścią tego, co jest zamknięciem ani jak działa. Występuje dodatkowe zamieszanie, ponieważ zamknięcia działają inaczej w językach funkcjonalnych, takich jak ML, gdzie zmienne są powiązane raczej z wartościami niż z przestrzenią pamięci, zapewniając stały strumień ludzi, którzy rozumieją zamknięcia w pewien sposób (mianowicie „podłączanie”), czyli po prostu niepoprawny dla JavaScript, gdzie zmienne są zawsze powiązane z miejscem do przechowywania, a nigdy z wartościami.
** Każda funkcja zewnętrzna, jeśli kilka jest zagnieżdżonych lub nawet w kontekście globalnym, jak wyraźnie wskazuje ta odpowiedź .
źródło
first_calculator
jest to obiekt (a nie funkcja), nie należy używać nawiasówsecond_calculator = first_calculator;
, ponieważ jest to przypisanie, a nie wywołanie funkcji. Aby odpowiedzieć na twoje pytanie, byłoby tylko jedno wywołanie make_calculator, więc wykonano by tylko jeden kalkulator, a zmienne first_calculator i second_calculator odnosiłyby się do tego samego kalkulatora, więc odpowiedzi byłyby 3, 403, 4433, 44330.Jak wytłumaczyłbym to sześciolatkowi:
Wiesz, jak dorośli mogą posiadać dom, a oni nazywają go domem? Kiedy mama ma dziecko, to tak naprawdę nic nie ma, prawda? Ale jego rodzice są właścicielami domu, więc za każdym razem, gdy ktoś pyta dziecko „Gdzie jest twój dom?”, Może odpowiedzieć „ten dom!” I wskazać dom jego rodziców. „Zamknięcie” to zdolność dziecka do tego, by zawsze (nawet za granicą) móc powiedzieć, że ma dom, mimo że tak naprawdę to rodzice są właścicielami domu.
źródło
Czy możesz wyjaśnić zamknięcia 5-latkowi? *
Nadal uważam, że wyjaśnienia Google działają bardzo dobrze i są zwięzłe:
* Pytanie AC #
źródło
Lepiej uczę się przez porównania DOBRY / ZŁY. Lubię widzieć działający kod, a po nim niedziałający kod, który ktoś może napotkać. Ułożyła się jsFiddle że robi porównanie i stara się sprowadzić do różnic najprostszych wyjaśnień mogę wymyślić.
Zamknięcia wykonane poprawnie:
W powyższym kodzie
createClosure(n)
wywoływany jest przy każdej iteracji pętli. Zauważ, że nazwałem zmienną,n
aby podkreślić, że jest to nowa zmienna utworzona w nowym zakresie funkcji i nie jest tą samą zmienną,index
która jest związana z zewnętrznym zakresem.Tworzy to nowy zakres i
n
jest związany z tym zakresem; oznacza to, że mamy 10 oddzielnych zakresów, po jednym dla każdej iteracji.createClosure(n)
zwraca funkcję, która zwraca nw tym zakresie.W ramach każdego zakresu
n
jest związany z dowolną wartością, jaką miał, kiedycreateClosure(n)
został wywołany, więc funkcja zagnieżdżona, która zostanie zwrócona, zawsze zwróci wartość tegon
, co miała podczascreateClosure(n)
wywołania.Zamknięcia wykonane nieprawidłowo:
W powyższym kodzie pętla została przesunięta w obrębie
createClosureArray()
funkcji, a teraz funkcja zwraca tylko ukończoną tablicę, co na pierwszy rzut oka wydaje się bardziej intuicyjne.To, co może nie być oczywiste, to fakt, że
createClosureArray()
wywoływane jest tylko wtedy, gdy dla tej funkcji tworzony jest tylko jeden zakres zamiast jednego dla każdej iteracji pętli.W ramach tej funkcji
index
definiowana jest zmienna o nazwie . Pętla działa i dodaje funkcje do zwracanej tablicyindex
. Zauważ, żeindex
jest zdefiniowany wcreateClosureArray
funkcji, która jest wywoływana tylko raz.Ponieważ w
createClosureArray()
funkcji był tylko jeden zakres ,index
jest on związany tylko z wartością w tym zakresie. Innymi słowy, za każdym razem, gdy pętla zmienia wartośćindex
, zmienia ją dla wszystkiego, co odnosi się do niej w tym zakresie.Wszystkie funkcje dodane do tablicy zwracają
index
zmienną SAME z zakresu nadrzędnego, w którym została zdefiniowana, zamiast 10 różnych z 10 różnych zakresów, takich jak pierwszy przykład. W rezultacie wszystkie 10 funkcji zwraca tę samą zmienną z tego samego zakresu.Po zakończeniu i zakończeniu
index
modyfikacji pętli wartość końcowa wynosiła 10, dlatego każda funkcja dodana do tablicy zwraca wartość pojedynczejindex
zmiennej, która jest teraz ustawiona na 10.Wynik
źródło
let
dovar
naprawia różnicę.n
utworzone w nowym zamknięciu. Zwracamy funkcję, abyśmy mogli zapisać ją w tablicy i wywołać ją później.arr[index] = (function (n) { return 'n = ' + n; })(index);
. Ale potem przechowujesz wynikowy ciąg w tablicy zamiast funkcji do wywołania, która pokonuje punkt mojego przykładu.Wikipedia na temat zamknięć :
Technicznie rzecz biorąc, w JavaScripcie , każda funkcja jest zamknięcie . Zawsze ma dostęp do zmiennych zdefiniowanych w otaczającym zakresie.
Ponieważ konstrukcja definiująca zakres w JavaScript jest funkcją , a nie blokiem kodu jak w wielu innych językach, to co zwykle rozumiemy przez zamknięcie w JavaScript to funkcja działająca ze zmiennymi nielokalnymi zdefiniowanymi w już wykonanej funkcji otaczającej .
Zamknięcia są często używane do tworzenia funkcji z ukrytymi prywatnymi danymi (ale nie zawsze tak jest).
ems
W powyższym przykładzie użyto anonimowej funkcji, która została wykonana raz. Ale nie musi tak być. Można go nazwać (np.
mkdb
) I wykonać później, generując funkcję bazy danych przy każdym wywołaniu. Każda wygenerowana funkcja będzie miała swój własny ukryty obiekt bazy danych. Innym przykładem użycia zamknięć jest sytuacja, gdy nie zwracamy funkcji, ale obiekt zawierający wiele funkcji do różnych celów, z których każda ma dostęp do tych samych danych.źródło
Przygotowałem interaktywny samouczek JavaScript, aby wyjaśnić, jak działają zamknięcia. Co to jest zamknięcie?
Oto jeden z przykładów:
źródło
Sekretami funkcji JavaScript są zmienne prywatne
Za każdym razem, gdy go wywołujesz, tworzona jest zmienna lokalna „name”, której nadawana jest nazwa „Mary”. I za każdym razem, gdy funkcja wychodzi, zmienna zostaje utracona, a nazwa zostaje zapomniana.
Jak można się domyślić, ponieważ zmienne są tworzone ponownie przy każdym wywołaniu funkcji i nikt ich nie pozna, musi istnieć tajne miejsce, w którym są przechowywane. To może być nazywany Komnata Tajemnic lub stosu lub zakresu lokalnej , ale to naprawdę nie ma znaczenia. Wiemy, że gdzieś tam są ukryte w pamięci.
Ale w JavaScript jest ta szczególna rzecz, że funkcje tworzone wewnątrz innych funkcji mogą również znać lokalne zmienne swoich rodziców i utrzymywać je tak długo, jak żyją.
Tak długo, jak jesteśmy w funkcji nadrzędnej, może ona tworzyć jedną lub więcej funkcji potomnych, które współużytkują tajne zmienne z tajnego miejsca.
Ale smutne jest to, że jeśli dziecko jest również prywatną zmienną funkcji rodzica, umrze również, gdy rodzic skończy, a sekrety umrą wraz z nimi.
Aby żyć, dziecko musi wyjść, zanim będzie za późno
A teraz, chociaż Maryja „już nie biegnie”, pamięć o niej nie zostaje utracona, a jej dziecko zawsze pamięta swoje imię i inne tajemnice, które dzielili podczas wspólnego pobytu.
Jeśli więc nazwiesz dziecko „Alice”, ona odpowie
To wszystko, co można powiedzieć.
źródło
Nie rozumiem, dlaczego odpowiedzi są tutaj tak złożone.
Oto zamknięcie:
Tak. Prawdopodobnie używasz tego wiele razy dziennie.
źródło
a
. Moim zdaniem, niespodzianką jest to, że obiekt zakresu JS skutecznie zapewniaa
raczej właściwość niż stałą. I zauważysz to ważne zachowanie tylko, jeśli je zmodyfikujesz, jak wreturn a++;
Przykład pierwszego punktu autorstwa dlaliberte:
źródło
Zamknięcie ma miejsce, gdy funkcja wewnętrzna ma dostęp do zmiennych w swojej funkcji zewnętrznej. To prawdopodobnie najprostsze jedno-liniowe wyjaśnienie, jakie można uzyskać w przypadku zamknięć.
źródło
Wiem, że istnieje już wiele rozwiązań, ale sądzę, że ten mały i prosty skrypt może być przydatny do zademonstrowania koncepcji:
źródło
Przespałeś się i zaprosiłeś Dana. Mówisz Danowi, żeby przyniósł jeden kontroler XBox.
Dan zaprasza Paula. Dan prosi Paula, aby przyniósł jednego kontrolera. Ilu kontrolerów przywieziono na imprezę?
źródło
Autor „ Zamknięć” całkiem dobrze wyjaśnił zamknięcia, wyjaśniając powód, dla którego ich potrzebujemy, a także wyjaśniając środowisko leksykalne, które jest niezbędne do zrozumienia zamknięć.
Oto podsumowanie:
Co się stanie, jeśli zmienna jest dostępna, ale nie jest lokalna? Jak tutaj:
W takim przypadku interpreter znajduje zmienną w
LexicalEnvironment
obiekcie zewnętrznym .Proces składa się z dwóch etapów:
Gdy funkcja jest tworzona, otrzymuje ukrytą właściwość o nazwie [[Zakres]], która odwołuje się do bieżącego LexicalEnvironment.
Jeśli zmienna zostanie odczytana, ale nigdzie nie można jej znaleźć, generowany jest błąd.
Funkcje zagnieżdżone
Funkcje można zagnieżdżać jeden w drugim, tworząc łańcuch środowisk LexicalEnvironments, który można również nazwać łańcuchem zasięgu.
Tak więc funkcja g ma dostęp do g, a i f.
Domknięcia
Funkcja zagnieżdżona może nadal działać po zakończeniu funkcji zewnętrznej:
Oznaczanie środowisk leksykalnych:
Jak widzimy,
this.say
jest właściwością w obiekcie użytkownika, więc nadal działa po zakończeniu użytkownika.A jeśli pamiętasz, kiedy
this.say
jest tworzony, to (jak każda funkcja) otrzymuje wewnętrzne odniesieniethis.say.[[Scope]]
do bieżącego LexicalEnvironment. Tak więc środowisko leksykalne bieżącego wykonania użytkownika pozostaje w pamięci. Wszystkie zmienne użytkownika również są jego właściwościami, więc są one również starannie przechowywane, a nie jak zwykle usuwane.Chodzi o to, aby zapewnić, że jeśli funkcja wewnętrzna chce uzyskać dostęp do zmiennej zewnętrznej w przyszłości, jest w stanie to zrobić.
Podsumowując:
To się nazywa zamknięcie.
źródło
Funkcje JavaScript mogą uzyskać dostęp do:
Jeśli funkcja uzyskuje dostęp do swojego środowiska, oznacza to zamknięcie.
Pamiętaj, że funkcje zewnętrzne nie są wymagane, ale oferują korzyści, których nie omawiam tutaj. Dzięki dostępowi do danych w jego otoczeniu zamknięcie utrzymuje te dane przy życiu. W podklasie funkcji zewnętrznych / wewnętrznych funkcja zewnętrzna może tworzyć dane lokalne i ostatecznie wychodzić, a jednak, jeśli jakakolwiek funkcja wewnętrzna (funkcje) przetrwają po wyjściu funkcji zewnętrznej, wówczas funkcja (funkcje wewnętrzne) zachowują lokalne dane funkcji zewnętrznej żywy.
Przykład zamknięcia korzystającego ze środowiska globalnego:
Wyobraź sobie, że zdarzenia przycisku Przepełnienie stosu Głosuj w górę i Głosuj w dół są implementowane jako zamknięcia, głosowanie w górę i głosowanie w dół, które mają dostęp do zmiennych zewnętrznych isVotedUp i isVotedDown, które są zdefiniowane globalnie. (Dla uproszczenia mam na myśli przyciski pytań głosowania StackOverflow, a nie tablicę przycisków odpowiedzi głosowaniem).
Gdy użytkownik kliknie przycisk Głosowania, funkcja głosowania sprawdza, czy isVotedDown == true, aby ustalić, czy głosować w górę, czy po prostu anulować głosowanie w dół. Funkcja głosowania U__kliknij jest zamknięciem, ponieważ uzyskuje dostęp do swojego środowiska.
Wszystkie cztery z tych funkcji są zamknięciami, ponieważ wszystkie mają dostęp do swojego środowiska.
źródło
Jako ojciec 6-latka, który obecnie uczy małych dzieci (i względnego nowicjusza w kodowaniu bez formalnego wykształcenia, więc konieczne będą poprawki), myślę, że lekcja najlepiej trzymałaby się praktycznej zabawy. Jeśli sześciolatek jest gotowy zrozumieć, co to jest zamknięcie, to są one na tyle duże, że same mogą spróbować. Sugerowałbym wklejenie kodu do jsfiddle.net, wyjaśnienie i pozostawienie ich samych w celu stworzenia wyjątkowej piosenki. Poniższy tekst wyjaśniający jest prawdopodobnie bardziej odpowiedni dla 10-latka.
INSTRUKCJE
DANE: Dane to zbiór faktów. Mogą to być liczby, słowa, pomiary, obserwacje, a nawet tylko opisy rzeczy. Nie możesz go dotknąć, powąchać ani posmakować. Możesz to zapisać, wypowiedzieć i usłyszeć. Możesz go użyć do stworzenia zapachu i smaku dotykowego za pomocą komputera. Może być przydatny przez komputer za pomocą kodu.
KOD: Cały powyższy zapis nazywa się kodem . Jest napisany w JavaScript.
JAVASCRIPT: JavaScript jest językiem. Podobnie jak angielski, francuski lub chiński to języki. Istnieje wiele języków, które są rozumiane przez komputery i inne procesory elektroniczne. Aby JavaScript mógł być zrozumiany przez komputer, potrzebuje tłumacza. Wyobraź sobie, że nauczyciel, który mówi tylko po rosyjsku, przychodzi uczyć twoich uczniów w szkole. Kiedy nauczyciel mówi „все садятся”, klasa nie zrozumie. Ale na szczęście masz w klasie rosyjskiego ucznia, który mówi wszystkim, że to znaczy „wszyscy usiądźcie” - więc wszyscy to robicie. Klasa jest jak komputer, a rosyjski uczeń jest tłumaczem. W przypadku JavaScript najpopularniejszym tłumaczem jest przeglądarka.
PRZEGLĄDARKA: Kiedy łączysz się z Internetem na komputerze, tablecie lub telefonie, aby odwiedzić stronę internetową, korzystasz z przeglądarki. Przykłady, które możesz znać, to Internet Explorer, Chrome, Firefox i Safari. Przeglądarka może zrozumieć JavaScript i powiedzieć komputerowi, co musi zrobić. Instrukcje JavaScript są nazywane funkcjami.
FUNKCJA: Funkcja w JavaScript jest jak fabryka. Może to być mała fabryka z tylko jedną maszyną w środku. Lub może zawierać wiele innych małych fabryk, każda z wieloma maszynami wykonującymi różne zadania. W prawdziwej fabryce ubrań możesz mieć do czynienia z ryzami materiału i szpulkami nici oraz z koszulkami i dżinsami. Nasza fabryka JavaScript przetwarza tylko dane, nie może szyć, wiercić dziury ani topić metalu. W naszej fabryce JavaScript dane wchodzą i wychodzą.
Wszystkie te dane brzmią trochę nudno, ale jest naprawdę bardzo fajne; możemy mieć funkcję, która mówi robotowi, co zrobić na obiad. Powiedzmy, że zapraszam ciebie i twojego przyjaciela do mojego domu. Najbardziej lubisz udka z kurczaka, lubię kiełbaski, twój przyjaciel zawsze chce tego, co chcesz, a mój przyjaciel nie je mięsa.
Nie mam czasu na zakupy, więc funkcja musi wiedzieć, co mamy w lodówce, aby podejmować decyzje. Każdy składnik ma inny czas gotowania i chcemy, aby robot podawał wszystko na gorąco jednocześnie. Musimy dostarczyć tej funkcji dane o tym, co lubimy, funkcja może „rozmawiać” z lodówką, a funkcja może kontrolować robota.
Funkcja zwykle ma nazwę, nawiasy i nawiasy klamrowe. Lubię to:
Zauważ, że
/*...*/
i//
przestań czytać kod przez przeglądarkę.NAZWA: Możesz wywołać funkcję niemal z dowolnego słowa, które chcesz. Przykład „cookMeal” jest typowy dla połączenia dwóch słów razem i nadania drugiemu dużej litery na początku - ale nie jest to konieczne. Nie może mieć spacji i nie może być liczbą samą w sobie.
RODZICE: „Nawiasy” lub
()
skrzynka na listy w drzwiach fabryki funkcji JavaScript lub skrzynka pocztowa na ulicy do wysyłania pakietów informacji do fabryki. Czasami skrzynka pocztowa może być na przykład oznaczonacookMeal(you, me, yourFriend, myFriend, fridge, dinnerTime)
, w którym to przypadku wiesz, jakie dane musisz podać.SZACIONKI: „Szelki”, które wyglądają tak, to
{}
przyciemnione szyby naszej fabryki. Z wnętrza fabryki widać, ale z zewnątrz nie widać.PRZYKŁAD POWYŻSZEGO KODU
Nasz kod zaczyna się od funkcji słowa , więc wiemy, że jest jedna! Następnie nazwa funkcji śpiewa - to mój własny opis tego, o czym jest funkcja. Następnie nawiasy () . Nawiasy są zawsze dostępne dla funkcji. Czasami są one puste, a czasami mają coś w tym jeden ma słowa w.:
(person)
. Po tym jest taki nawias klamrowy{
. Oznacza to początek funkcji sing () . Ma partnera, który oznacza koniec sing () w ten sposób}
Ta funkcja może mieć coś wspólnego ze śpiewaniem i może wymagać pewnych danych o osobie. Zawiera instrukcje, jak zrobić coś z tymi danymi.
Teraz, po funkcji sing () , pod koniec kodu jest linia
ZMIENNE: Litery var oznaczają „zmienna”. Zmienna jest jak obwiednia. Na zewnątrz ta koperta jest oznaczona „osoba”. W środku znajduje się kartka papieru z informacją, której potrzebuje nasza funkcja, niektóre litery i spacje połączone razem jak kawałek sznurka (zwanego sznurkiem), który tworzy frazę „starsza pani”. Nasza koperta może zawierać inne rzeczy, takie jak liczby (nazywane liczbami całkowitymi), instrukcje (nazywane funkcjami), listy (nazywane tablicami ). Ponieważ ta zmienna jest zapisywana poza wszystkimi nawiasami klamrowymi
{}
i ponieważ możesz zobaczyć przez przyciemnione okna, gdy jesteś w nawiasach klamrowych, ta zmienna jest widoczna z dowolnego miejsca w kodzie. Nazywamy to „zmienną globalną”.ZMIENNA GLOBALNA: osoba jest zmienną globalną, co oznacza, że jeśli zmienisz jej wartość z „starszej damy” na „młodego mężczyznę”, osoba ta pozostanie młodym mężczyzną, dopóki nie zdecydujesz się go zmienić ponownie i że każda inna funkcja w kod widzi, że to młody człowiek. Naciśnij F12przycisk lub spójrz na ustawienia Opcje, aby otworzyć konsolę programisty przeglądarki i wpisz „osoba”, aby zobaczyć, jaka jest ta wartość. Wpisz,
person="a young man"
aby go zmienić, a następnie ponownie wpisz „osoba”, aby zobaczyć, że zmienił się.Po tym mamy linię
Ta linia wywołuje funkcję, tak jakby to był pies
Gdy przeglądarka załaduje kod JavaScript i osiągnie ten wiersz, uruchomi funkcję. Kładę wiersz na końcu, aby upewnić się, że przeglądarka ma wszystkie informacje potrzebne do jej uruchomienia.
Funkcje definiują akcje - główna funkcja polega na śpiewaniu. Zawiera zmienną o nazwie firstPart, która odnosi się do śpiewu o osobie, która dotyczy każdego z wierszy utworu: „Była” + osoba + ”, która przełknęła”. Jeśli wpiszesz firstPart w konsoli , nie otrzymasz odpowiedzi, ponieważ zmienna jest zablokowana w funkcji - przeglądarka nie widzi wewnątrz przyciemnionych okien nawiasów klamrowych.
ZAMKNIĘCIA: Zamknięcia to mniejsze funkcje znajdujące się w funkcji big sing () . Małe fabryki w dużej fabryce. Każdy z nich ma własne nawiasy klamrowe, co oznacza, że zmiennych w nich nie można zobaczyć z zewnątrz. Dlatego nazwy zmiennych ( stworzenie i wynik ) mogą być powtarzane w zamknięciach, ale z różnymi wartościami. Jeśli wpiszesz te nazwy zmiennych w oknie konsoli, nie uzyskasz ich wartości, ponieważ są ukryte przez dwie warstwy przyciemnionego okna.
Wszystkie zamknięcia wiedzą, czym jest zmienna funkcji sing () o nazwie firstPart , ponieważ mogą widzieć z ich przyciemnionych okien.
Po zamknięciach pojawiają się linie
Funkcja sing () wywoła każdą z tych funkcji w podanej kolejności. Następnie praca funkcji sing () zostanie zakończona.
źródło
Dobra, rozmawiając z 6-letnim dzieckiem, prawdopodobnie użyłbym następujących skojarzeń.
Porównaj z sytuacją, gdy drzwi były zamknięte przez przeciąg i nikt nie był w środku (wykonanie funkcji ogólnej), a następnie doszło do lokalnego pożaru i spalenia pomieszczenia (pojemnik na śmieci: D), a następnie zbudowano nowe pomieszczenie i teraz możesz wyjść inne zabawki (nowa instancja funkcji), ale nigdy nie otrzymuj tych samych zabawek, które pozostały w instancji pierwszego pokoju.
Dla zaawansowanego dziecka umieściłbym coś takiego. Nie jest idealny, ale sprawia, że czujesz, co to jest:
Jak widać, zabawki pozostawione w pokoju są nadal dostępne dla brata i bez względu na to, czy pokój jest zamknięty. Oto jsbin do zabawy.
źródło
Odpowiedź dla sześciolatka (zakładając, że wie, czym jest funkcja, czym jest zmienna i jakie dane):
Funkcje mogą zwracać dane. Jednym rodzajem danych, które możesz zwrócić z funkcji, jest inna funkcja. Gdy ta nowa funkcja zostanie zwrócona, wszystkie zmienne i argumenty użyte w funkcji, która ją utworzyła, nie znikają. Zamiast tego ta funkcja nadrzędna „zamyka się”. Innymi słowy, nic nie może zajrzeć do jego wnętrza i zobaczyć użytych zmiennych, z wyjątkiem funkcji, którą zwrócił. Ta nowa funkcja ma specjalną możliwość spojrzenia wstecz na funkcję, która ją utworzyła i zobaczenia danych w niej zawartych.
Innym bardzo prostym sposobem wyjaśnienia tego jest zakres:
Za każdym razem, gdy utworzysz mniejszy zakres w większym zakresie, mniejszy zakres zawsze będzie mógł zobaczyć, co jest w większym zakresie.
źródło
Funkcja w JavaScript to nie tylko odwołanie do zestawu instrukcji (jak w języku C), ale zawiera również ukrytą strukturę danych, która składa się z odwołań do wszystkich używanych zmiennych nielokalnych (zmienne przechwycone). Takie dwuczęściowe funkcje nazywane są zamknięciami. Każda funkcja w JavaScript może być uważana za zamknięcie.
Zamknięcia są funkcjami ze stanem. Jest nieco podobny do „tego” w tym sensie, że „to” zapewnia również stan funkcji, ale funkcja i „to” są osobnymi obiektami („to” jest tylko fantazyjnym parametrem i jedynym sposobem na powiązanie go na stałe z funkcja polega na utworzeniu zamknięcia). Chociaż „to” i funkcja zawsze działają osobno, nie można oddzielić funkcji od jej zamknięcia, a język nie zapewnia dostępu do przechwyconych zmiennych.
Ponieważ wszystkie te zmienne zewnętrzne, do których odwołuje się funkcja zagnieżdżona leksykalnie, są w rzeczywistości zmiennymi lokalnymi w łańcuchu jej funkcji leksykalnie obejmujących (można przyjąć, że zmienne globalne są zmiennymi lokalnymi niektórych funkcji root), a każde pojedyncze wykonanie funkcji tworzy nowe wystąpienia z jego zmiennych lokalnych wynika, że każde wykonanie funkcji zwracającej (lub w inny sposób ją przenoszącej, takiej jak rejestracja jako wywołanie zwrotne) funkcja zagnieżdżona tworzy nowe zamknięcie (z własnym potencjalnie unikalnym zestawem zmiennych nielokalnych, które reprezentują jej wykonanie kontekst).
Należy również zrozumieć, że zmienne lokalne w JavaScript są tworzone nie w ramce stosu, ale na stercie i niszczone tylko wtedy, gdy nikt się do nich nie odwołuje. Gdy funkcja powraca, odwołania do jej zmiennych lokalnych są zmniejszane, ale nadal mogą mieć wartość inną niż null, jeśli podczas bieżącego wykonywania staną się częścią zamknięcia i nadal będą się do nich odwoływać jej funkcje zagnieżdżone leksykalnie (co może się zdarzyć tylko wtedy, gdy odwołania do te zagnieżdżone funkcje zostały zwrócone lub w inny sposób przeniesione do jakiegoś zewnętrznego kodu).
Przykład:
źródło
Być może trochę ponad wszystko, z wyjątkiem najbardziej przedwczesnych z sześciolatków, ale kilka przykładów, które pomogły mi sprawić, aby koncepcja zamknięcia w JavaScript została dla mnie kliknięta.
Zamknięcie to funkcja, która ma dostęp do zakresu innej funkcji (jej zmiennych i funkcji). Najłatwiejszym sposobem utworzenia zamknięcia jest użycie funkcji w ramach funkcji; dlatego, że w JavaScript funkcja zawsze ma dostęp do zakresu funkcji zawierającej.
ALERT: małpa
W powyższym przykładzie wywoływana jest funkcja zewnętrzna, która z kolei wywołuje funkcję wewnętrzną. Zwróć uwagę, w jaki sposób externalVar jest dostępny dla innerFunction, o czym świadczy prawidłowe alarmowanie o wartości outerVar.
Teraz rozważ następujące kwestie:
ALERT: małpa
referenceToInnerFunction jest ustawiony na outerFunction (), który po prostu zwraca odwołanie do innerFunction. Gdy wywoływana jest funkcja referenceToInnerFunction, zwraca wartość externalVar. Ponownie, jak powyżej, pokazuje to, że innerFunction ma dostęp do outerVar, zmiennej zewnętrznej funkcji. Co więcej, warto zauważyć, że zachowuje ten dostęp nawet po zakończeniu działania zewnętrznej funkcji.
I tutaj sprawy stają się naprawdę interesujące. Jeśli mielibyśmy pozbyć się externalFunction, powiedzmy, ustaw go na null, możesz pomyśleć, że referenToInnerFunction utraciłby dostęp do wartości outerVar. Ale tak nie jest.
ALERT: małpa ALERT: małpa
Ale jak to się dzieje? W jaki sposób referenToInnerFunction może nadal znać wartość outerVar, skoro externalFunction ma wartość null?
Powodem, dla którego referenceToInnerFunction może nadal uzyskiwać dostęp do wartości outerVar, jest to, że kiedy zamknięcie zostało utworzone po raz pierwszy przez umieszczenie wewnętrznej funkcji wewnątrz funkcji zewnętrznej, funkcja wewnętrzna dodała odniesienie do zakresu funkcji zewnętrznej (jej zmiennych i funkcji) do łańcucha zasięgu. Oznacza to, że innerFunction ma wskaźnik lub odniesienie do wszystkich zmiennych outerFunction, w tym outerVar. Tak więc nawet po zakończeniu wykonywania funkcji zewnętrznej lub nawet po jej usunięciu lub ustawieniu wartości NULL, zmienne w jej zakresie, takie jak outerVar, pozostają w pamięci z powodu wyjątkowego odwołania do nich ze strony funkcji wewnętrznej, do której zwrócono wartość referenceToInnerFunction. Aby naprawdę uwolnić outerVar i resztę zmiennych outerFunction z pamięci, musiałbyś pozbyć się tych wyjątkowych odniesień do nich,
//////////
Dwie inne rzeczy na temat zamknięć, na które należy zwrócić uwagę. Po pierwsze, zamknięcie zawsze będzie miało dostęp do ostatnich wartości jego zawierającej funkcji.
ALERT: goryl
Po drugie, po utworzeniu zamknięcia zachowuje on odwołanie do wszystkich zmiennych i funkcji swojej funkcji zamykającej; nie można wybierać. A jednak zamknięcia powinny być stosowane oszczędnie, a przynajmniej ostrożnie, ponieważ mogą wymagać dużej ilości pamięci; wiele zmiennych może być przechowywanych w pamięci długo po zakończeniu wykonywania funkcji zawierającej.
źródło
Po prostu wskazałbym je na stronie Zamknięcia Mozilli . To najlepsze, najbardziej zwięzłe i proste wyjaśnienie podstaw zamknięcia i praktycznego zastosowania, jakie znalazłem. Jest wysoce zalecane każdemu, kto uczy się JavaScript.
I tak, poleciłbym to nawet sześciolatkowi - jeśli sześciolatek uczy się o zamknięciach, to logiczne, że są gotowi zrozumieć zwięzłe i proste wyjaśnienie podane w artykule.
źródło