Dlaczego zamknięcie jest ważne dla JavaScript?

16

Wyrażenie lambda w języku C # ma również zamknięcia, ale rzadko jest omawiane przez społeczności lub książki w języku C #. Widzę o wiele więcej osób zajmujących się JavaScript i książek mówi o jego zamknięciach niż w świecie C #. Dlaczego?

TomCaps
źródło
3
Ponieważ JavaScript jest traktowany jako język funkcjonalny pierwszej klasy, a C # jest klasycznym językiem OOP. Różne paradygmaty, różne style programowania.
Raynos
1
Posiadanie funkcji pierwszej klasy sprawia, że ​​język jest językiem funkcjonalnym. PHP (od 5.3), python lub D ma funkcję pierwszej klasy. Czy powiedziałbyś, że są to języki funkcjonalne? Z pewnością nie i javascript.
deadalnix
1
@deadalnix JavaScript to język oparty na wielu paradygmatach. Obsługuje głównie proceduralny, funkcjonalny i prototypowy paradygmat OO. W żadnym wypadku nie jest to czysty język funkcjonalny, ale można łatwo pisać javascript w funkcjonalnym paradygmacie bez zginania języka. Powiedziałbym to samo o C # i python.
Raynos
3
@deadalnix, język, który pozwala zdefiniować kombinator Y, jest językiem funkcjonalnym. Zgodnie z definicją.
SK-logic
1
@deadalnix: Przypuszczam, że sprowadza się do pytania, jakie podejścia promuje język. Istnieje wiele frameworków i platform JavaScript (w szczególności node.js i DOM (model zdarzeń)), które w dużym stopniu opierają się na funkcjach pierwszego rzędu. PHP ma funkcje, które pozwalają na ten sam styl programowania, ale jeśli spojrzysz na standardowe API lub wiele frameworków, zobaczysz, że tak naprawdę nie jest to rdzeń języka.
back2dos

Odpowiedzi:

5

Ponieważ javascript nie ma takiej funkcji, jak przestrzenie nazw, i można dość łatwo zepsuć wszystkie obiekty globalne.

Dlatego ważne jest, aby móc izolować część kodu we własnym środowisku wykonawczym. Zamknięcie jest do tego idealne.

Takie użycie zamknięcia nie ma sensu w języku takim jak C #, w którym masz przestrzenie nazw, klasy itd. Do izolowania kodu i nie umieszczania wszystkiego w zasięgu globalnym.

Bardzo powszechną praktyką dla kodu javascript jest pisanie go w następujący sposób:

(function(){
    // Some code
})();

Jak widać, jest to deklaracja funkcji anonimowej, po której następuje natychmiastowe wykonanie. Zatem do wszystkiego zdefiniowanego w funkcji nie można uzyskać dostępu z zewnątrz, a Ty nie zepsujesz globalnego zasięgu. Kontekst wykonania tej funkcji pozostanie aktywny, dopóki użyje go jakiś kod, np. Funkcje zagnieżdżone zdefiniowane w tym kontekście, które można przekazać jako wywołanie zwrotne lub cokolwiek innego.

JavaScript jest językiem zupełnie innym niż C #. Nie jest zorientowany obiektowo, jest zorientowany prototypowo. To prowadzi do bardzo różnych praktyk na końcu.

W każdym razie zamknięcia są dobre, więc używaj ich, nawet w C #!

EDYCJA: Po krótkiej dyskusji na czacie stackoverflow, myślę, że należy odpowiedzieć na tę odpowiedź.

Funkcja w przykładowym kodzie nie jest zamknięciem. Jednak ta funkcja może definiować zmienne lokalne i funkcje zagnieżdżone. Wszystkie zagnieżdżone funkcje korzystające z tych zmiennych lokalnych są zamknięciami.

Jest to przydatne do udostępniania niektórych danych w zestawie funkcji bez naruszania globalnego zasięgu. Jest to najczęstsze użycie zamknięcia w javascript.

Zamykanie jest znacznie potężniejsze niż udostępnianie takich danych, ale bądźmy realistami, większość programistów nie wie nic o programowaniu funkcjonalnym. W języku C # użyłbyś klasy lub przestrzeni nazw do tego rodzaju zastosowań, ale JS nie zapewnia tej funkcji.

Z zamknięciem możesz zrobić znacznie więcej niż tylko chronić globalny zasięg, ale to właśnie zobaczysz w kodzie źródłowym JS.

deadalnix
źródło
9
To nie jest zamknięcie. Opisujesz zalety używania funkcji do tworzenia zasięgu lokalnego.
Raynos
2
zamknięcie jest zamknięciem tylko wtedy, gdy zamyka się nad zmiennymi swobodnymi. Dziękuję również za poinformowanie mnie, że nie wiem, jak działa JavaScript :)
Raynos
2
zamiast uciekać się do podważania mnie, nazywając mnie początkującym, możesz wysuwać rzeczywiste argumenty w pokoju JavaScript SO Chat . Ta dyskusja tak naprawdę nie należy tutaj, ale chętnie omówię twoje nieporozumienia na czacie SO.
Raynos
1
Doceniony. JavaScript jest zorientowany obiektowo. Po prostu nie jest oparty na klasach, ale taką funkcjonalność można łatwo utworzyć. Ma pierwszorzędne funkcje, wykorzystuje zamknięcia, aby naturalnie pasować do paradygmatów silnie napędzanych zdarzeniami, a głównym źródłem inspiracji według autora, Brendana Eicha, był Scheme. Nie jestem do końca pewien, czym według ciebie jest język funkcjonalny, ale zabawne jest to, że wydaje ci się, że dziedziczenie klasowe jest w jakiś sposób krytyczne, a nie całkowicie konieczne, kiedy możesz przekazywać funkcje i stosować je w nowych kontekstach.
Erik Reppen,
2
-1 również. To nie opisuje zamknięć, opisuje zakres.
Izkata,
12

Prawdopodobnie dlatego, że popularne implementacje orientacji obiektowej JavaScript zależą od zamknięć. Spójrzmy na prosty przykład:

function counter() {
   var value = 0;
   this.getValue = function() { return value; }
   this.increase = function() { value++; }
}

var myCounter = new counter();
console.log(myCounter.getValue());
myCounter.increase();
console.log(myCounter.getValue());

Metody getValuei increasefaktycznie są zamknięciami, zamykającymi zmienną value.

użytkownik 281377
źródło
3
-1 „zależy od zamknięć” To bardzo subiektywne. To nie.
Raynos
Raynos: chcesz pokazać nam, jak tworzyć i używać członków prywatnych bez zamknięć?
user281377,
1
@ammoQ w JavaScript nie ma czegoś takiego jak prywatny. Miałeś na myśli „ponieważ JavaScript klasyczna emulacja OO zależy od zamknięć”. JavaScript OO jest prototypowy, co stanowi inny paradygmat. To dobra odpowiedź na pytanie: „Używamy zamknięć w JavaScript, ponieważ trzeba było emulować klasyczne OO”
Raynos
1
@ keppla Miałem na myśli, że „OO zależy od zamknięć” jest subiektywne. W ogóle nie potrzebujesz zamknięć dla OO.
Raynos
2
ujawniasz
4

Ponieważ wiele bibliotek, dzięki którym JavaScript jest „znośny”, korzysta z nich.

Spójrz na JQuery , Prototype lub MooTools , żeby wymienić tylko trzy popularne. Każda z tych bibliotek udostępnia eachmetodę dla swoich kolekcji jako preferowany sposób iteracji, który wykorzystuje funkcję iteracji. Ta funkcja, podczas uzyskiwania dostępu do wartości w zakresie zewnętrznym, będzie zamknięciem:

[1,2,3].each(function(item) {
   console.log(item);
});

I na tym się nie kończy: wywołania zwrotne w Ajaxie, obsługa zdarzeń w Ext.js itp. Wszystkie przyjmują funkcje, a jeśli nie chcesz nadpisywać kodu setkami funkcji, które są wywoływane dokładnie raz, zamykanie jest właściwym rozwiązaniem.

keppla
źródło
1
Dlaczego to nie jest zamknięcie? console.logjest zdefiniowany w zewnętrznym zakresie, podobnie consolejak „dowolna zmienna”. I dlaczego w efekcie powstaje pętla for, która nie robi nic innego, zła praktyka?
keppla
@Raynos: moja definicja zamknięcia to „blok zawierający wolną zmienną”, nie traci tego statusu, wolna zmienna okazuje się być na najwyższym poziomie. Ale aby wesprzeć mój punkt, rozważ tę metodę: pastie.org/2144689 . Dlaczego to powinno być złe?
keppla
Nadal uważam, że „zamykanie” danych globalnych jest uważane za oszustwo. Zgadzam się, że dostęp do danych w górę łańcucha zasięgu jest w porządku, po prostu dobrą praktyką jest ograniczenie tego dostępu.
Raynos
Uzgodniono, aby zminimalizować dostęp
keppla
Należy zauważyć, że dzięki iteratorom tablicowym (forEach, map, redukcja itp.) Nie ma żadnego powodu, aby były zamknięciami, wszystkie ich stany przechodziły bezpośrednio do nich.
Uważałbym,
3

Zgadzam się z innymi odpowiedziami, że „dobre” kodowanie w JavaScript jest bardziej zależne od zamknięć. Jednak oprócz tego prawdopodobnie chodzi tylko o to, jak długo ta funkcja działa w każdym języku. JavaScript ma w zasadzie zamknięcia od najwcześniejszych wdrożeń. Z drugiej strony C # ma je tylko od wersji 3.0, która została wydana wraz z Visual Studio 2008.

Wielu programistów C #, z którymi się spotkałem, nadal pracuje nad projektami 2.0. I nawet jeśli pracują w wersji 3.0 lub 4.0, często nadal używają idiomów 2.0. Uwielbiam zamknięcia; miejmy nadzieję, że będą one częściej używane w języku C #, gdy koncepcja rozprzestrzenia się wśród programistów C #.

RationalGeek
źródło
2

Głównym powodem jest to, że C # jest typowo statyczny, a funkcje mają dostęp tylko do jednego, z góry określonego środowiska, co utrudnia użyteczność zamknięć.

Z drugiej strony, w JavaScript, zamknięcia pozwalają funkcjom zachowywać się jak metody, gdy są dostępne jako właściwość obiektu, a będąc obiektami pierwszej klasy, można je również wstrzykiwać do różnych środowisk, które umożliwiają działanie podprogramów w różnych kontekstach.

Filip Dupanović
źródło
1
Co ma z tym wspólnego pisanie statyczne? Zamknięcia są ortogonalne.