Jak działają domknięcia w JavaScript, prostym językiem angielskim
JavaScript jest szeroko przyjętym językiem, którego możesz użyć do zbudowania czegokolwiek, od prostej strony docelowej po aplikację klasy produkcyjnej, full-stack. Wraz z rozwojem JavaScript i programowania w ogóle, programiści zdali sobie sprawę, że paradygmat programowania zorientowanego obiektowo (OOP) jest niepożądany w większości przypadków użycia. Programowanie funkcyjne pojawiło się jako rozwiązanie wielu bolączek związanych z OOP.
Zamknięcia są szeroko dyskutowanym tematem w świecie programowania funkcyjnego, ale często są one definiowane luźno i w technicznym żargonie. Zrobimy co w naszej mocy, aby wyjaśnić jak działają zamknięcia w JavaScript w terminologii laika.
Pod koniec tego poradnika, powinieneś zrozumieć:
- Jak zidentyfikować domknięcia
- Czym jest domknięcie i jak zachowuje się w stosunku do kontekstu wykonania i stosu wywołań
- Powszechne przypadki użycia domknięć
Zrozumienie domknięć w JavaScript
Zaczniemy od pokazania jak wygląda domknięcie.
Spróbuj uruchomić ten kod samodzielnie. Technicznie rzecz biorąc, funkcja o nazwie makeCounter
zwraca inną funkcję o nazwie increment
. Ta funkcja increment
ma dostęp do zmiennej count
nawet po wykonaniu funkcji makeCount
. Częścią zamknięcia jest tutaj zmienna count
; jest ona dostępna dla funkcji increment
, gdy zostanie zdefiniowana, nawet po tym, jak makeCounter
zakończy działanie. Druga część to funkcja increment
.
Wyobraź sobie, że masz dom i ogród, który go otacza. Gdy otworzysz drzwi do ogrodu i zamkniesz je, nie możesz ich już otworzyć ponownie – ogród staje się niedostępny. Jesteś głodny, a na szczęście w ogrodzie rośnie drzewo pomarańczowe i jabłoń. Bierzesz mały woreczek, zrywasz pomarańczę i jabłko i wracasz do domu. Pamiętaj, że nie możesz już wrócić na zewnątrz.
Teraz, kiedy już jesteś w swoim domu, możesz wyjąć pomarańczę lub jabłko z torby i zjeść je, kiedy tylko zgłodniejesz. Mała torebka w tym przykładzie jest zamknięciem. Zamknięcie zawiera wszystkie zmienne i funkcje, które były dostępne dla ciebie, gdy byłeś w ogrodzie, nawet jeśli jesteś w domu i nie możesz wyjść na zewnątrz.
Zobaczmy, jak to wygląda w kodzie:
Ponieważ zmienna fruits
jest dostępna dla zwracanej funkcji, gdy makeFruitGarden
jest wykonywana, zmienna fruits
i funkcja wewnętrzna stają się zamknięciem. Kiedy consumeFruit
jest wykonywany, zwracany jest fruit
– ostatni element z tablicy fruits
, ponieważ pop()
jest używany. Gdy oba owoce zostaną skonsumowane/zjedzone, nie pozostanie nic do zjedzenia.
Zrozumienie zakresu leksykalnego
Aby naprawdę zrozumieć domknięcia, powinieneś znać termin „zakres”. Zakres leksykalny to wymyślne określenie bieżącego środowiska względem tego, do czego się odnosisz.
W poniższym przykładzie zakres zmiennej o nazwie myName
nazywany jest „zakresem globalnym”.
// global scopeconst myName = "John Doe"function displayName() { // local/function scope console.log(myName);};displayName()
Mogłeś zobaczyć tę koncepcję odniesioną podczas czytania o tym, jak var
nie jest block-scoped i jak const
let
jest. Ważne jest, aby pamiętać, że w JavaScript funkcja zawsze tworzy swój własny zakres. Nazywa się to zakresem local
lub function
, jak pokazano w przykładzie kodu.
Jeśli zwracałeś uwagę, możesz myśleć, że myName
i displayName
są częścią zamknięcia. Miałbyś rację! Ale ponieważ funkcja i zmienna tutaj istnieją w zakresie globalnym, nie ma zbyt wiele wartości w nazywaniu tego zamknięciem.
W JavaScript istnieje wiele rodzajów zakresów, ale jeśli chodzi o zamknięcia, istnieją trzy zakresy, które powinieneś znać:
- Zakres globalny jest domyślnym zakresem, w którym wszyscy żyją. Pomyśl o nim jak o swojej ulicy
- Zewnętrzny zakres funkcji jest funkcją, która zwraca funkcję. Ma ona swój własny zakres. Pomyśl o tym jak o swoim ogrodzie
- Wewnętrzny/lokalny zakres funkcji to zwrócona funkcja, która staje się zamknięciem. Pomyśl o tym jak o swoim domu
Teraz zagłębmy się w kilka przypadków użycia.
Wspólne przypadki użycia domknięć
Currying
Funkcja currying jest kolejną potężną koncepcją w programowaniu funkcyjnym. Aby zaimplementować funkcję curry w JavaScript, użyjesz domknięć.
Currying funkcji może być opisany jako przekształcanie funkcji i jest wykonywany w ten sposób: add(1, 2, 3)
do add(1)(2)(3)
.
function add(a) { return function(b) { return function(c) { return a + b + c; }; };};add(1)(2)(3) // returns 6
Funkcja add
przyjmuje pojedynczy argument, a następnie zwraca dwie funkcje, które są zagnieżdżone jedna za drugą. Celem currying jest wzięcie kilku argumentów i w końcu skończenie z pojedynczą wartością.
Funkcje wyższego rzędu
Celem funkcji wyższego rzędu jest wzięcie funkcji jako argumentu i zwrócenie wyniku. Metody tablicowe takie jak map
i reduce
są przykładami funkcji wyższego rzędu.
const arrayOfNumbers = ;const displayNumber = (num) => { console.log(num);}arrayOfNumbers.forEach(displayNumber)
Funkcja wyższego rzędu Array.prototype.forEach
przyjmuje tutaj displayNumber
jako argument, a następnie wykonuje go dla każdego elementu w arrayOfNumbers
. Jeśli używałeś frameworka UI, takiego jak Vue lub React, możesz być zaznajomiony z komponentami wyższego rzędu, które są zasadniczo tym samym, co funkcje wyższego rzędu.
Jaka jest więc różnica między funkcjami wyższego rzędu a curryingiem? Podczas gdy funkcja wyższego rzędu przyjmuje funkcję jako argument i zwraca wartość, funkcja curried zwraca funkcję jako wynik, co ostatecznie prowadzi do wartości.
Menedżery elementów DOM
Jest to popularny wzorzec projektowy często używany do uzyskiwania i ustawiania właściwości elementów DOM. W poniższym przykładzie, stworzymy menedżera elementów do stylizacji elementów.
makeStyleManager
zwraca obiekt, który daje dostęp do dwóch funkcji, które są częścią domknięcia obok zmiennych element
i currentStyles
. Nawet po tym, jak makeStyleManager
zakończy wykonywanie, funkcje getStyle
i setStyle
mają dostęp do zmiennych.
Wniosek
Zamknięcia w języku JavaScript mogą być trudne do zrozumienia, nawet dla programistów z doświadczeniem zawodowym pod swoim paskiem. Zrozumienie domknięć ostatecznie uczyni cię lepszym programistą.
Powinieneś teraz być w stanie zidentyfikować domknięcie, gdy jest ono używane w bazie kodu, która wygląda dziwnie lub nie ma sensu. Zamknięcia są krytycznym pojęciem w programowaniu funkcjonalnym i mam nadzieję, że ten przewodnik pomógł Ci zrobić krok naprzód w Twojej podróży w kierunku ich opanowania.
LogRocket: Debugowanie błędów JavaScript łatwiejsze dzięki zrozumieniu kontekstu
Debugowanie kodu jest zawsze żmudnym zadaniem. Ale im lepiej rozumiesz swoje błędy, tym łatwiej jest je naprawić.
LogRocket pozwala Ci zrozumieć te błędy w nowy i unikalny sposób. Nasze rozwiązanie monitorujące frontend śledzi zaangażowanie użytkownika w Twój JavaScript frontend, aby dać Ci możliwość dowiedzenia się, co dokładnie zrobił użytkownik, co doprowadziło do błędu.
LogRocket zapisuje logi konsoli, czasy ładowania strony, ślady ścieżek, powolne żądania/odpowiedzi sieciowe z nagłówkami + ciałami, metadane przeglądarki oraz logi niestandardowe. Zrozumienie wpływu Twojego kodu JavaScript nigdy nie będzie łatwiejsze!
Wypróbuj go za darmo.
Dalsza lektura
- „Nigdy nie rozumiałem zamknięć JavaScript” – Dogłębny, techniczny przewodnik do zrozumienia, jak działa zamknięcie. Idealny dla kogoś, kto chce zrozumieć domknięcia w odniesieniu do
ExecutionContext
, stosu wywołań, środowiska leksykalnego, itp. - MDN reference for JavaScript closures – Szybki przewodnik po domknięciach (w tym niektóre powszechne pułapki) na wypadek, gdybyś potrzebował szybko wyczyścić swoją pamięć
- „Master the JavaScript Interview: What is a Closure?” – Domknięcia wyjaśnione przez Erica Elliota; świetne dla programistów, którzy rozumieją koncepcje programowania funkcyjnego