Funkcje, które zwracają funkcję


110

Utknąłem z koncepcją „funkcji, które zwracają funkcje”. Mam na myśli książkę „Object Oriented Javascript” autorstwa Stoyana Stefanova.

Snippet One:

    function a() {
      
        alert('A!');
    
        function b(){
            alert('B!'); 
        }
    
        return b();
    }
    
    var s = a();
    alert('break');
    s();

Wynik:

A!
B!
break

Snippet Two

function a() {
  
    alert('A!');

    function b(){
        alert('B!'); 
    }

    return b;
}

var s = a();
alert('break');
s();
Wynik:

A!
break
B!

Czy ktoś może mi powiedzieć, jaka jest różnica między powracaniem ba b()powyższymi fragmentami?


2
Zauważysz, że pierwszy fragment kodu wyświetla błąd w s ();
weirdalsuperfan

Odpowiedzi:


122

Przypisanie zmiennej do funkcji (bez nawiasów) powoduje skopiowanie odwołania do funkcji. Umieszczenie nawiasu na końcu nazwy funkcji wywołuje funkcję i zwraca wartość zwracaną przez funkcje.

Próbny

function a() {
    alert('A');
}
//alerts 'A', returns undefined

function b() {
    alert('B');
    return a;
}
//alerts 'B', returns function a

function c() {
    alert('C');
    return a();
}
//alerts 'C', alerts 'A', returns undefined

alert("Function 'a' returns " + a());
alert("Function 'b' returns " + b());
alert("Function 'c' returns " + c());

W swoim przykładzie definiujesz również funkcje w ramach funkcji. Jak na przykład:

function d() {
    function e() {
        alert('E');
    }
    return e;
}
d()();
//alerts 'E'

Funkcja jest nadal wywoływana. Nadal istnieje. Jest to używane w JavaScript przez cały czas. Funkcje można przekazywać tak jak inne wartości. Rozważ następujące:

function counter() {
    var count = 0;
    return function() {
        alert(count++);
    }
}
var count = counter();
count();
count();
count();

Funkcja count może przechowywać zmienne, które zostały zdefiniowane poza nią. Nazywa się to zamknięciem. Jest również często używany w JavaScript.


Snippet One: var hero = {name: 'Rafaelo', sayName: function () {return hero.name; }, nayName: hero.sayName} hero.nayName (); Snippet Two: var hero = {name: 'Rafaelo', sayName: function () {return hero.name; }, nayName: this.sayName} hero.nayName (); Pierwszy fragment kodu daje mi prawidłowe dane wyjściowe, podczas gdy drugi nie. Czemu? Pozdrowienia.
Cafecorridor

thisoznacza tylko coś wewnątrz ciała funkcji, w przeciwnym razie jest globalne. Mówisz o this.sayNametym, że chcesz zmiennej globalnej, sayNamektóra nie istnieje, jest niezdefiniowana, więc nie można jej wywołać.
kzh

8
Byłem zdezorientowany przez d () (); na początku, ale potem zdałem sobie sprawę, że first () wywołuje d, a second () wywołuje wartość zwracaną przez d, czyli e.
skud

1
Osiem lat później i nadal jest to aktualne!
Brad Vanderbush

45

Zwrócenie nazwy funkcji bez ()zwraca odniesienie do funkcji, które można przypisać tak, jak zostało to zrobione var s = a(). steraz zawiera odniesienie do funkcji b(), a wywołanie s()jest funkcjonalnie równoważne z wywołaniem b().

// Return a reference to the function b().
// In your example, the reference is assigned to var s
return b;

Wywołanie funkcji ()w instrukcji return powoduje wykonanie funkcji i zwraca wartość zwróconą przez funkcję. Jest to podobne do wywoływania var x = b();, ale zamiast przypisywać wartość zwracaną przez b()Ciebie, zwracasz ją z funkcji wywołującej a(). Jeśli b()sama funkcja nie zwraca wartości, wywołanie zwraca undefinedpo wykonaniu jakiejkolwiek innej pracy b().

// Execute function b() and return its value
return b();
// If b() has no return value, this is equivalent to calling b(), followed by
// return undefined;

1
Ze wszystkich odpowiedzi bardziej podobała mi się twoja odpowiedź ze względu na jej prostotę.
anar khalilov

Dobrze wyłożone! Bardzo łatwo zrozumieć tę odpowiedź.
AndrewSteinheiser

Dziękujemy za sprawdzenie bitu o tym, czy funkcja nie zwraca wartości, którą wywołanie zwraca undefined. Niedawno odkryłem to zamieszanie: nawet jeśli funkcja wyraźnie zwraca wartość null, jeśli przypiszesz wartość zwróconą do zmiennej, będzie ona niezdefiniowana, a nie zerowa. Wyobrażam sobie, że spowodowało to wiele dziwnych problemów w niektórych bazach kodu, ponieważ null i undefined nie są absolutnie równoważne z ===.
Benjamin

37

return b(); wywołuje funkcję b () i zwraca jej wynik.

return b; zwraca odniesienie do funkcji b, którą można przechowywać w zmiennej, aby wywołać ją później.


17

Powracający b to zwracanie obiektu funkcji. W Javascript funkcje są po prostu obiektami, jak każdy inny obiekt. Jeśli uznasz to za nieprzydatne, po prostu zastąp słowo „obiekt” słowem „rzecz”. Możesz zwrócić dowolny obiekt z funkcji. Możesz zwrócić wartość prawda / fałsz. Liczba całkowita (1, 2, 3, 4 ...). Możesz zwrócić ciąg. Możesz zwrócić złożony obiekt z wieloma właściwościami. Możesz zwrócić funkcję. funkcja to tylko rzecz.

W twoim przypadku zwracanie bzwraca rzecz, rzecz jest funkcją wywoływalną. Zwracanie b()zwraca wartość zwróconą przez funkcję wywoływalną.

Rozważ ten kod:

function b() {
   return 42;
}

Korzystając z powyższej definicji, return b();zwraca wartość 42. Z drugiej strony return b;zwraca funkcję, która sama zwraca wartość 42. Są to dwie różne rzeczy.


4
powinno powrócić 42;)
c69

4

Kiedy wracasz b, jest to po prostu odwołanie do funkcji b, ale nie jest ona obecnie wykonywana.

Kiedy wracasz b(), wykonujesz funkcję i zwracasz jej wartość.

Spróbuj alerting typeof(s)w swoich przykładach. Fragment b da ci „funkcję”. Co da ci wycinek?


Pierwsza daje „undefined”. Czy to oznacza, że ​​return b () jest całkowicie bezużyteczne? Ponadto w drugim fragmencie funkcja b jest prywatna. Jak więc możemy uzyskać dostęp do odniesienia poza funkcją? Jeśli to możliwe, podaj mi link, który jasno wyjaśnia tę koncepcję. Dzięki!
Cafecorridor

Otrzymałem odpowiedź na pierwszą. zwraca 1 + 2 w funkcji b (), a typeof pokazuje liczbę. Dzięki.
Cafecorridor

Cieszę się, że to rozgryzłeś! Jeśli chodzi o funkcję prywatną: w drugim przykładzie nie jest ona tak naprawdę prywatna, ponieważ już ją zwróciłeś. W rzeczywistości zostaje przypisany do s. Spróbuj return thiszamiast return btego… Będziesz wtedy mógł s.b();)
vzwick

Na pewno spróbuję. Nie osiągnąłem jeszcze tej koncepcji w Javascript. Może za kilka dni. Dzięki! :)
Cafecorridor

funkcja a () {alert ("A!"); funkcja b () {alert ("B!"); } return b; } var s = a (); usunąć; s (); ---- koniec ---- Czy pojęcie odwołania w Javascript jest takie samo jak w Javie? Tutaj usunąłem funkcję a (), a jednak wywołanie s () wykonuje b (). Czy mogę więc powiedzieć, że s zawiera kopię b i nie wskazuje na b () zdefiniowane w a ()?
Cafecorridor

2

Wyobraź sobie funkcję jako typ, jak int. W funkcji można zwracać wartości int. Możesz również zwrócić funkcje, są one obiektami typu „function”.

Teraz problem składni: ponieważ funkcje zwracają wartości, jak można zwrócić funkcję, a nie zwracającą wartość?

pomijając nawiasy! Ponieważ bez nawiasów funkcja nie zostanie wykonana! Więc:

return b;

Zwróci „funkcję” (wyobraź sobie, że zwracasz liczbę), podczas gdy:

return b();

Najpierw wykonuje funkcję, a następnie zwraca wartość uzyskaną przez jej wykonanie, to duża różnica!


W drugim fragmencie funkcja b jest prywatna. Jak więc możemy uzyskać dostęp do odniesienia poza funkcją? Czy są jakieś zasady dotyczące tego samego?
Cafecorridor

Obiekty w JavaScript (w tym funkcje) nie są już prywatne, jeśli je udostępniasz.
kzh

@Cafecorridor: Jeśli funkcja prywatna jest zwracana przez coś (funkcja publiczna) lub jest przypisana do zmiennej publicznej (no cóż, zmiennej dostępnej dla aplikacji), możesz łatwo zrobić yourvariable (); , w przeciwnym razie przypisz zwróconą funkcję do zmiennej i ponownie wykonaj twoją zmienną ();
Francesco Belladonna

2

Utwórz zmienną :

var thing1 = undefined;

Zadeklaruj funkcję :

function something1 () {
    return "Hi there, I'm number 1!";
}

Zaalarmować wartości z thing1(nasza pierwsza zmienna):

alert(thing1); // Outputs: "undefined".

Teraz, gdybyśmy chcieli thing1być odniesieniem do funkcji something1, co oznacza, że ​​byłoby to to samo, co nasza utworzona funkcja, zrobilibyśmy:

thing1 = something1;

Jeśli jednak chcieliśmy mieć return wartość funkcji, musimy przypisać jej wartość zwracaną przez wykonywaną funkcję. Funkcję wykonujesz używając nawiasów:

thing1 = something1(); // Value of thing1: "Hi there, I'm number 1!" 

-1

Fragment pierwszy:

function a() {
  
    alert('A!');

    function b(){
        alert('B!'); 
    }

    return b(); //return nothing here as b not defined a return value
}

var s = a(); //s got nothing assigned as b() and thus a() return nothing.
alert('break');
s(); // s equals nothing so nothing will be executed, JavaScript interpreter will complain

wyrażenie „b ()” oznacza wykonanie funkcji o nazwie „b”, która pokazuje okno dialogowe z tekstem „B!”

instrukcja „return b ();” oznacza wykonanie funkcji o nazwie „b”, a następnie zwrócenie funkcji „b”. ale „b” nic nie zwraca, to instrukcja „return b ()” też nic nie zwraca. Jeśli b () zwraca liczbę, to „return b ()” też jest liczbą.

Teraz „s” ma przypisaną wartość tego, co „a ()” return, co zwraca „b ()”, czyli nic, więc „s” jest niczym (w JavaScript jest to właściwie rzecz, jest to „niezdefiniowana”). kiedy poprosisz JavaScript, aby zinterpretował typ danych „s”, interpreter JavaScript powie, że „s” jest niezdefiniowane.) Ponieważ „s” jest niezdefiniowane, kiedy poprosisz JavaScript o wykonanie tej instrukcji „s ()”, prosisz JavaScript o wykonanie funkcji o nazwie „s”, ale „s” to „nieokreślona”, a nie funkcja, więc JavaScript będzie narzekać, „hej, s nie jest funkcją, nie wiem jak do zrobienia z tym s ”, wówczas komunikat o błędzie„ Uncaught TypeError: s is not a function ”zostanie wyświetlony przez JavaScript (testowane w przeglądarkach Firefox i Chrome)


Snippet Two

function a() {
  
    alert('A!');

    function b(){
        alert('B!'); 
    }

    return b; //return pointer to function b here
}

var s = a();  //s get the value of pointer to b
alert('break');
s(); // b() function is executed

teraz funkcja „a” zwraca wskaźnik / alias do funkcji o nazwie „b”. więc gdy wykonujemy 's = a ()', 's' otrzyma wartość wskazującą na b, tj. 's' jest teraz aliasem 'b', wywołanie 's' równa się wywołaniu 'b'. tj. „s” jest teraz funkcją. Wykonanie „s ()” oznacza uruchomienie funkcji „b” (tak samo jak wykonanie „b ()”), okno dialogowe pokazujące „B!” pojawi się (tzn. uruchomi się 'alert (' B! '); instrukcja w funkcji' b ')


-2

Jest to bardzo przydatne w prawdziwym życiu.

Praca z Express.js

Twoja zwykła expresstrasa wygląda więc tak:

function itWorksHandler( req, res, next ) {
  res.send("It works!");
}

router.get("/check/works", itWorksHandler );

Ale co, jeśli musisz dodać jakieś opakowanie, moduł obsługi błędów lub coś?

Następnie wywołujesz swoją funkcję z opakowania.

function loggingWrapper( req, res, next, yourFunction ) {
  try {
    yourFunction( req, res );
  } catch ( err ) {
    console.error( err );
    next( err );
  }
}

router.get("/check/works", function( req, res, next ) {
  loggingWrapper( req, res, next, itWorksHandler );
});

Wygląda na skomplikowane? A co powiesz na to:

function function loggingWrapper( yourFunction ) => ( req, res, next ) {
  try {
    yourFunction( req, res, next );
  } catch ( err ) {
    console.error( err );
    next( err );
  }
}

router.get("/check/works", loggingWrapper( itWorksHandler ) );

Na końcu przekazujesz funkcję, loggingWrapperktórej jeden argument jest inną funkcją itWorksHandler, a loggingWrapperzwracasz nową funkcję, która przyjmuje req, res, nextjako argumenty.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.