Samouczek JavaScript: Funkcje wyższego rzędu

W zeszłym tygodniu od niechcenia zrezygnowałem z terminu „funkcja wyższego rzędu”, kiedy mówiłem o zapamiętywaniu. Chociaż teraz czuję się swobodnie, omijając takie terminy, nie zawsze wiedziałem, co oznaczają. W tym tygodniu zbadamy, czym są funkcje wyższego rzędu, pokażemy kilka typowych przykładów i dowiemy się, jak tworzyć własne.

W istocie funkcja wyższego rzędu to po prostu funkcja, która przyjmuje funkcję jako argument lub zwraca funkcję. Jest to możliwe w JavaScript dzięki pierwszorzędnym funkcjom, co oznacza, że ​​funkcje w JavaScript mogą być przekazywane jak każda inna zmienna. Chociaż brzmi to dość prosto, nie przekazuje do końca rodzaju mocy, jaką masz dzięki funkcjom pierwszej klasy.

Jeśli piszesz JavaScript, prawdopodobnie korzystałeś z funkcji wyższego rzędu i nawet tego nie zauważyłeś. Jeśli kiedykolwiek zamieniłeś forpętlę na metodę tablicową, używałeś funkcji wyższego rzędu. Jeśli kiedykolwiek korzystałeś z wyników wywołania AJAX (bez async/ await), korzystałeś z funkcji wyższego rzędu (obietnice i wywołania zwrotne obejmują funkcje wyższego rzędu). Jeśli kiedykolwiek napisałeś komponent Reacta, który wyświetla listę elementów, używałeś funkcji wyższego rzędu. Zobaczmy te przykłady:

elementy const = [„a”, „b”, „c”, „d”, „e”]

// Zamiast tego for loop ....

for (niech i = 0; i <items.length - 1; i ++) {

  console.log (pozycje [i]);

}

// Możemy użyć forEach, funkcji wyższego rzędu

// (forEach przyjmuje funkcję jako argument)

items.forEach ((item) => console.log (item));

// Callback lub obietnice, jeśli robisz

// asynchroniczne żądania, których używasz

// funkcje wyższego rzędu

get ('// aws.random.cat/meow', (response) => {

  putImageOnScreen (response.file);

});

get ('// random.dog/woof.json').then((response) => {

  putImageOnScreen (response.file);

});

// W komponencie React poniżej używana jest mapa,

// która jest funkcją wyższego rzędu

const myListComponent = (props) => {

  powrót (

   

          {props.items.map ((item) => {

            powrót (

  • {pozycja}
  • )

          })}

      );

    };

To są przykłady funkcji wyższego rzędu, które akceptują funkcje jako argumenty, ale wiele z nich zwraca również funkcje. Jeśli kiedykolwiek widziałeś wywołanie funkcji, które ma dwa zestawy nawiasów, jest to funkcja wyższego rzędu. Kiedyś takie rzeczy były mniej powszechne, ale jeśli w ogóle pracujesz z Redux, prawdopodobnie korzystałeś z connectfunkcji, która jest funkcją wyższego rzędu:

eksportuj domyślne połączenie (mapStateToProps, mapDispatchToProps) (MyComponent);

W powyższym przypadku wywołujemy connectz dwoma argumentami i zwraca funkcję, którą natychmiast wywołujemy z jednym argumentem. Być może widziałeś (lub napisałeś) prostą bibliotekę rejestrowania, która używa funkcji jako wartości zwracanych. W poniższym przykładzie utworzymy program rejestrujący, który rejestruje swój kontekst przed komunikatem:

const createLogger = (context) => {

  return (msg) => {

    console.log (`$ {kontekst}: $ {msg}`);

  }

};

const log = createLogger ('myFile');

log ('Bardzo ważna wiadomość');

// wylogowuje „myFile: bardzo ważna wiadomość”

Powyższy przykład zaczyna ilustrować pewną moc funkcji wyższego rzędu (zobacz także mój poprzedni post dotyczący zapamiętywania). Zauważ, że createLoggerprzyjmuje argument, do którego odwołujemy się w treści funkcji, którą zwracamy. Ta zwrócona funkcja, którą przypisujemy do zmiennej log, nadal może uzyskać dostęp do contextargumentu, ponieważ znajdowała się w zakresie, w którym funkcja została zdefiniowana.

Ciekawostka: Odniesienia contextsą możliwe dzięki domknięciom. Nie będę się tutaj zajmował zamknięciami, ponieważ zasługują na własny post, ale można ich używać w połączeniu z funkcjami wyższego rzędu, aby uzyskać naprawdę interesujące efekty.

Na przykład używanie domknięć wraz z funkcjami wyższego rzędu było jedynym sposobem, w jaki mogliśmy mieć „prywatne” lub odporne na manipulacje zmienne w JavaScript:

let protectedObject = (function () {

  let myVar = 0;

  powrót {

    get: () => myVar,

    przyrost: () => myVar ++,

  };

}) ();

protectedObject.get (); // zwraca 0

protectedObject.increment ();

protectedObject.get (); // zwraca 1

myVar = 42; // ups! właśnie utworzyłeś zmienną globalną

protectedObject.get (); // nadal zwraca 1

Nie dajmy się jednak ponieść emocjom. Funkcje wyższego rzędu nie wymagają niczego wymyślnego jak domknięcia. Są to po prostu funkcje, które przyjmują inne funkcje jako argumenty lub zwracają funkcje. Kropka. Jeśli potrzebujesz więcej przykładów lub dalszych lektur, zajrzyj do rozdziału o funkcjach wyższego rzędu w „Eloquent JavaScript” autorstwa Marijna Haverbeke.

Pytania lub komentarze? Zapraszam do kontaktu na Twitterze: @freethejazz.