Jakie są korzyści z używania funkcji anonimowych zamiast nazwanych dla wywołań zwrotnych i parametrów w kodzie zdarzeń JavaScript?

Jestem nowy w JavaScript. Rozumiem wiele pojęć tego języka, czytam o prototypowym modelu dziedziczenia, i zaostrzam gwizdek coraz bardziej interaktywnymi rzeczami front-endowymi. To ciekawy język, ale zawsze jestem trochę wyłączony przez spaghetti zwrotne, które jest typowe dla wielu nietrywialnych modeli interakcji.

Coś, co zawsze wydawało mi się dziwne, jest to, że pomimo koszmaru czytelności, który jest gniazdem JavaScript zagnieżdżone wywołania zwrotne, jedyną rzeczą, którą bardzo rzadko widzę w wielu przykładach i samouczkach, jest użycie predefiniowanych funkcji nazwanych jako argumentów wywołania zwrotnego. Z dnia na dzień jestem programistą Javy i odrzucam stereotypowe gadki o nazwach Enterprise-y dla jednostek kodu jedną z rzeczy, które sprawiły mi przyjemność w pracy w języku z dużym wyborem funkcji IDE, jest to, że używanie znaczących, jeśli długich nazw może znacznie wyjaśnić intencję i znaczenie kodu bez uczynienia go bardziej zrozumiałym trudno być produktywnym. Dlaczego więc nie użyć tego samego podejścia podczas pisania kodu JavaScript?

Rozważając to, mogę wymyślić argumenty, które są zarówno za, jak i przeciw tej idei, ale moja naiwność i nowość w języku uniemożliwiają mi wyciąganie wniosków, dlaczego byłoby to dobre na poziomie technicznym.

Plusy:

  • elastyczność. Asynchroniczna funkcja z parametrem callback może być osiągnięta przez jedną z wielu różnych ścieżek kodu i to może być trudniej napisać nazwaną funkcję, aby uwzględnić każdy możliwy przypadek krawędzi.
  • Speed. To gra mocno w mentalności hakerów. Przykręć do niego rzeczy, dopóki nie zadziała.
  • Everyone else is doing it
  • mniejsze rozmiary plików, nawet jeśli są trywialne, ale w sieci liczy się każdy bit.
  • prostsze AST? Zakładam, że funkcje anonimowe są generowane w czasie wykonywania i tak JIT nie będzie sypać z mapowaniem nazwy do instrukcji, ale jestem tylko zgaduję.
  • szybsza wysyłka? Tego też nie jestem pewien. Znowu zgaduję.

Wady:

  • to ohydne i nieczytelne
  • dodaje to zamieszania, gdy jesteś zagnieżdżony głęboko w bagnie wywołań zwrotnych (co, szczerze mówiąc, prawdopodobnie oznacza, że piszesz źle skonstruowany kod, ale jest to dość powszechne).
  • dla kogoś bez tła funkcjonalnego może to być dziwaczne pojęcie do grok

Przy tak wielu nowoczesnych przeglądarkach pokazujących zdolność do wykonywania kodu JavaScript znacznie szybciej niż wcześniej, nie widzę, jak trywialny zysk wydajności można dostać się za pomocą anonimowych wywołań zwrotnych byłoby koniecznością. Wydaje się, że jeśli znajdujesz się w sytuacji, w której użycie nazwanej funkcji jest możliwe (przewidywalne zachowanie i ścieżka wykonania), nie ma powodu, aby tego nie robić.

Więc czy są jakieś techniczne powody lub Gotcha, których nie jestem świadomy to sprawia, że ta praktyka jest tak powszechna z jakiegoś powodu?

Author: Cœur, 2012-04-23

5 answers

Używam funkcji anonimowych z trzech powodów:

  1. Jeśli nie jest potrzebna nazwa, ponieważ funkcja jest wywoływana tylko w jednym miejscu, to po co dodawać nazwę do dowolnej przestrzeni nazw, w której się znajdujesz.
  2. Funkcje anonimowe są zadeklarowane w linii, a funkcje inline mają zalety, ponieważ mogą uzyskać dostęp do zmiennych w zakresach nadrzędnych. Tak, możesz umieścić nazwę na funkcji anonimowej, ale zwykle jest to bezcelowe, jeśli jest zadeklarowana w linii. Więc inline ma znaczącą przewagę i jeśli robisz inline, nie ma powodu, aby umieścić na nim nazwę.
  3. kod wydaje się bardziej samodzielny i czytelny, gdy programy obsługi są zdefiniowane bezpośrednio w kodzie, który je wywołuje. Możesz czytać kod niemal sekwencyjnie, zamiast szukać funkcji o tej nazwie.

Staram się unikać głębokiego zagnieżdżania anonimowych funkcji, ponieważ można to zrozumieć i przeczytać. Zazwyczaj, gdy tak się dzieje, istnieje lepszy sposób na uporządkowanie kodu (czasami z pętlą, czasami z tabelą danych, itp...) i funkcje nazwane też zwykle nie są rozwiązaniem.

Myślę, że dodam, że jeśli wywołanie zwrotne zaczyna mieć więcej niż około 15-20 linii długości i nie wymaga bezpośredniego dostępu do zmiennych w zakresie rodzica, chciałbym pokusić się o nadanie mu nazwy i rozbicie go na własną nazwaną funkcję zadeklarowaną gdzie indziej. Jest tu zdecydowanie punkt czytelności, w którym nietrywialna funkcja, która staje się długa, jest po prostu bardziej można go konserwować, jeśli zostanie umieszczony we własnej jednostce nazwanej. Ale większość wywołań zwrotnych, które kończę, nie jest tak długa i uważam, że jest bardziej czytelna, aby utrzymać je w linii.

 38
Author: jfriend00,
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-30 16:46:00

Sam wolę funkcje nazwane, ale dla mnie sprowadza się to do jednego pytania:

Czy będę używać tej funkcji gdzie indziej?

Jeśli odpowiedź brzmi tak, nazwę / zdefiniuję ją. Jeśli nie, przekaż ją jako funkcję anonimową.

Jeśli użyjesz go tylko raz, nie ma sensu tłoczyć nim globalnej przestrzeni nazw. W dzisiejszych złożonych front-endach liczba nazwanych funkcji, które mogły być anonimowe, szybko rośnie (łatwo ponad 1000 na naprawdę skomplikowanych projektach), powoduje to (stosunkowo) duży wzrost wydajności dzięki preferowaniu funkcji anonimowych.

Jednak niezwykle ważna jest również konserwacja kodu. Każda sytuacja jest inna. Jeśli nie piszesz wielu z tych funkcji na początek, nie ma nic złego w robieniu tego tak czy inaczej. To zależy od Twoich preferencji.

Kolejna notka o imionach. Przyzwyczajenie się do definiowania długich nazw naprawdę zaszkodzi rozmiarowi pliku. Weźmy poniższy przykład.

Załóżmy, że oba te funkcje robią to samo:

function addTimes(time1, time2)
{
    // return time1 + time2;
}

function addTwoTimesIn24HourFormat(time1, time2)
{
    // return time1 + time2;
}

Drugi mówi dokładnie, co robi w nazwie. Pierwszy jest bardziej niejednoznaczny. Istnieje jednak 17 znaków różnicy w nazwie. Powiedzmy, że funkcja jest wywoływana 8 razy w całym kodzie, czyli 153 dodatkowe bajty twój kod nie musiał mieć. Nie kolosalne, ale jeśli jest to nawyk, ekstrapolowanie tego do 10 lub nawet 100 funkcji będzie łatwo oznaczać kilka KB różnicy w pobieraniu.

Znowu jednak konserwacja musi być ważona w stosunku do korzyści płynących z wydajności. To jest ból radzenia sobie z językiem skryptowym.

 10
Author: Andrew Ensley,
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-04-23 16:08:30

Cóż, żeby było jasne dla dobra moich argumentów, poniżej znajdują się wszystkie anonimowe funkcje/wyrażenia funkcyjne w mojej książce:

var x = function(){ alert('hi'); },

indexOfHandyMethods = {
   hi: function(){ alert('hi'); },
   high: function(){
       buyPotatoChips();
       playBobMarley();
   }
};

someObject.someEventListenerHandlerAssigner( function(e){
    if(e.doIt === true){ doStuff(e.someId); }
} );

(function namedButAnon(){ alert('name visible internally only'); })()

Plusy:

  • Może to zmniejszyć trochę cruft, szczególnie w funkcjach rekurencyjnych (gdzie można (powinno się faktycznie od argumentów.callee jest przestarzały) nadal używa nazwanego odwołania w ostatnim przykładzie wewnętrznie), i wyjaśnia, że funkcja jest uruchamiana tylko w tym jednym miejscu.

  • Czytelność kodu: w przykład dosłownego obiektu z funkcjami anon przypisanymi jako metody, byłoby głupio dodać więcej miejsc do polowania i dziobania logiki w kodzie, gdy cały punkt tego dosłownego obiektu polega na rzuceniu jakiejś powiązanej funkcjonalności w tym samym dogodnie odwołanym miejscu. Podczas deklarowania metod publicznych w konstruktorze, mam tendencję do definiowania funkcji znakowanych inline, a następnie przypisywania jako referencji tego.to samo imię. To pozwala mi używać tych samych metod wewnętrznie bez ' To."cruft and sprawia, że kolejność definicji nie jest niepokojąca, gdy się do siebie dzwonią.

  • Przydatne do unikania niepotrzebnego zanieczyszczenia globalnej przestrzeni nazw - wewnętrzne przestrzenie nazw nie powinny jednak nigdy być tak szeroko wypełnione lub obsługiwane przez wiele zespołów jednocześnie, więc ten argument wydaje mi się nieco głupi.

  • Zgadzam się z wywołaniami zwrotnymi inline podczas ustawiania krótkich procedur obsługi zdarzeń. Głupio jest polować na funkcję 1-5 linii, zwłaszcza, że z JS i funkcją podnoszenia, definicje mogą znaleźć się wszędzie, nawet w tym samym pliku. To może się zdarzyć przez przypadek, bez niszczenia czegokolwiek i nie, nie zawsze masz kontrolę nad tym. Zdarzenia zawsze powodują wywołanie funkcji zwrotnej. Nie ma powodu, aby dodawać więcej linków do łańcucha nazw, które trzeba przeskanować, tylko do inżynierii wstecznej prostych procedur obsługi zdarzeń w dużej bazie kodowej, a problem śledzenia stosu można rozwiązać poprzez abstrakcję wyzwalaczy zdarzeń do metod, które rejestrują przydatne zdarzenia informacja kiedy jest włączony tryb debugowania i uruchom wyzwalacze. Zaczynam budować w ten sposób całe interfejsy.

  • Przydatne, gdy chcemy, aby kolejność definicji funkcji miała znaczenie. Czasami chcesz mieć pewność, że domyślna funkcja jest taka, jak myślisz, aż do pewnego punktu w kodzie, w którym można ją przedefiniować. Albo chcesz, aby złamanie było bardziej oczywiste, gdy zależności zostaną przetasowane.

Wady:

  • Anonymous nie mogą skorzystać z funkcja podnoszenia. To duża różnica. Często korzystam z podnoszenia, aby zdefiniować własne, jawnie nazwane funkcje i konstruktory obiektów w kierunku dołu, a następnie przejść do definicji obiektu i rzeczy typu main-loop na górze. Uważam, że to sprawia, że kod jest łatwiejszy do odczytania, gdy dobrze nazwiesz swoje var-y i uzyskasz szeroki wgląd w to, co się dzieje, zanim ctrl-Fing zdobędzie szczegóły tylko wtedy, gdy mają dla Ciebie znaczenie. Podnoszenie może być również ogromną korzyścią w interfejsach silnie sterowanych zdarzeniami, w których narzucając ścisłą kolejność tego, co jest dostępne, kiedy może cię ugryźć w tyłek. Hoisting ma swoje własne zastrzeżenia (takie jak circular reference potential), ale jest bardzo użytecznym narzędziem do porządkowania i czytelnego kodu, gdy jest używany prawidłowo.

  • Czytelność / Debugowanie. Absolutnie są one używane zbyt mocno czasami i może to sprawić, że debugowanie i czytelność kodu będą kłopotliwe. Codebases, które w dużej mierze opierają się na JQ, na przykład, może być poważnym PITA do odczytu i debugowania, jeśli nie enkapsulować prawie nieuniknione anon-ciężkie i masowo przeciążone args z $ zupa w rozsądny sposób. Na przykład metoda hover JQuery, jest klasycznym przykładem nadmiernego użycia funkcji anon, gdy upuszczasz do niej dwie funkcje anon, ponieważ początkujący może łatwo założyć, że jest to standardowa metoda przypisania słuchacza zdarzeń, a nie jedna metoda przeciążona, aby przypisać procedury obsługi dla jednego lub dwóch zdarzeń. $(this).hover(onMouseOver, onMouseOut) jest o wiele bardziej przejrzyste niż dwie funkcje anon.

 1
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
2012-05-02 22:28:41

Jest bardziej czytelny przy użyciu nazwanych funkcji i są one również zdolne do samodzielnego odwoływania się, jak w przykładzie poniżej.

(function recursion(iteration){
    if (iteration > 0) {
      console.log(iteration);
      recursion(--iteration);
    } else {
      console.log('done');
    }
})(20);

console.log('recursion defined? ' + (typeof recursion === 'function'));

Http://jsfiddle.net/Yq2WD/

Jest to miłe, gdy chcesz mieć natychmiast wywołaną funkcję, która odwołuje się do siebie, ale nie dodaje do globalnej przestrzeni nazw. Jest nadal czytelny, ale nie zanieczyszczający. Zjedz swoje ciasto.

Cześć, mam na imię Jason czy Cześć, mam na imię ???? ty wybierz.
 1
Author: Jason Sebring,
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-11-13 02:23:46

Trochę za późno na imprezę, ale niektóre jeszcze nie wymienione aspekty funkcji, anonimowe lub inne...

W humanoidalnych rozmowach o kodzie nie jest łatwo nawiązać do funkcji Anon. Np. " Joe, czy mógłbyś wyjaśnić, co algorytm robi, w ramach tej funkcji. ... Który? Siedemnasta funkcja anonimowa w funkcji fooApp. ... Nie, Nie ten! Siedemnasty!"

Funkcje Anon są również anonimowe dla debuggera. (duh!) Dlatego śledzenie stosu debuggera zazwyczaj pokazuje tylko znak zapytania lub podobny, co czyni go mniej użytecznym, gdy Ustawiłeś wiele punktów przerwania. Trafiłeś w punkt przerwania, ale przewijasz Okno debugowania w górę/w dół, aby dowiedzieć się, gdzie do cholery jesteś w swoim programie, ponieważ hej, funkcja znaku zapytania po prostu tego nie robi!

Obawy związane z zanieczyszczeniem globalnej przestrzeni nazw są ważne, ale można je łatwo rozwiązać, nazywając funkcje jako węzły wewnątrz własnego obiektu głównego, na przykład " myFooApp.happyFunc = function ( ... ) { ... }; ".

Funkcje, które są dostępne w globalnej przestrzeni nazw lub jako węzły w obiekcie root, jak powyżej, mogą być wywoływane bezpośrednio z debuggera, podczas programowania i debugowania. Np. w wierszu poleceń konsoli wykonaj " myFooApp.happyFunc (42)" Jest to niezwykle potężna zdolność, która nie istnieje (natywnie) w skompilowanych językach programowania. Spróbuj z Anon func.

Funkcje Anon mogą być bardziej czytelne, przypisując je do var, a następnie przekazując var jako callback (zamiast inlining). Np.: var funky = function ( ... ) { ... }; jQuery ('#otis').click (funky);

Korzystając z powyższego podejścia, możesz potencjalnie zgrupować kilka funkcji anon u góry funkcji rodzicielskiej, a następnie poniżej, mięso sekwencyjnych wypowiedzi staje się znacznie ściślej zgrupowane i łatwiejsze do odczytania.

 0
Author: RickS,
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
2016-11-20 16:54:52