Funkcje strzałkowe i callbacki

Jak przekazać programowi zasadę naliczania punktów? Przekaż funkcję. Strzałki pomogą Ci czytać map, filter i kod korzystający z callbacków.

CEL LEKCJI

Po tej lekcji potrafisz

  • Zapiszesz funkcję strzałkową w dwóch wariantach.
  • Przekażesz funkcję jako argument i opiszesz jej typ.
  • Użyjesz map, filter i forEach, rozpoznając ich różne wyniki.
WPROWADZENIE

Tryb gry zmienia punktację

Dzisiaj punkty liczymy podwójnie, jutro z premią 5. Zamiast wpisywać warunki w każdej rundzie, możemy przekazać regułę jako funkcję. Callback to po prostu funkcja przekazana innemu fragmentowi kodu do wywołania.

POZNAJ SKŁADNIĘ

Małe kroki, konkretne efekty

Strzałka w wersji krótkiej i z blokiem

Po lewej są parametry, po prawej działanie. Jedno wyrażenie po strzałce jest zwracane automatycznie. Jeśli otwierasz blok klamrami, wpisz return samodzielnie.

src/index.ts — osobny przykład
const podwoj = (n: number): number => n * 2;
const potroj = (n: number): number => {
  return n * 3;
};
console.log(podwoj(4), potroj(4));
wynik w konsoli
8 12

Przypisujemy funkcję do const, więc wywołujemy ją dopiero po tej deklaracji. To nie jest identyczne z deklaracją function, którą można wywołać wcześniej w jej zasięgu.

Funkcja jako argument

Typ (n: number) => number opisuje funkcję przyjmującą liczbę i zwracającą liczbę. Nawiasy przy regule w środku uruchamiają przekazaną funkcję.

src/index.ts — osobny przykład
function przelicz(n: number, regula: (n: number) => number): number {
  return regula(n);
}
console.log(przelicz(10, n => n * 2));
console.log(przelicz(10, n => n + 5));
wynik w konsoli
20
15

Nie dopisujemy typu przy drugim n: TypeScript odczytuje go z oczekiwanego typu parametru regula. To typowanie kontekstowe.

PRZYKŁAD Z OMÓWIENIEM

Tablica wyników po weekendowym bonusie

Najpierw zwiększamy wszystkie wyniki, potem wybieramy te uprawniające do nagrody. forEach służy wyłącznie do wypisania raportu.

src/index.ts — osobny przykład
const wyniki = [5, 12, 20];
const poBonusie = wyniki.map(punkty => punkty + 5);
const nagrody = poBonusie.filter(punkty => punkty >= 15);
nagrody.forEach((punkty, indeks) => {
  console.log("Nagroda " + (indeks + 1) + ": " + punkty);
});
console.log("Oryginał: " + wyniki.join(", "));
wynik w konsoli
Nagroda 1: 17
Nagroda 2: 25
Oryginał: 5, 12, 20

Jak program dochodzi do wyniku

  1. map wywołuje callback dla każdego elementu i zbiera jego wyniki do nowej tablicy: 10, 17, 25.
  2. filter zostawia elementy, dla których callback zwrócił true. Nie zamienia ich na wartości logiczne.
  3. forEach przekazuje element i jego indeks. Indeks zaczyna się od zera, więc numer nagrody zwiększamy o 1.
  4. Oryginał nie zmienił się: callbacki wykonują tu obliczenia na liczbach. Same map i filter nie gwarantują jednak, że callback nie zmodyfikuje obiektów.

Zanim przejdziesz dalej

Co stanie się po zamianie punkty => punkty + 5 na punkty => { punkty + 5; }?

Sprawdź swoje rozumowanie

Callback z blokiem nie zwraca wartości. map utworzy tablicę wartości undefined, a typ wyniku będzie void[]. Dodaj return lub usuń klamry.

EKSPERYMENT

Zmień kod i zobacz, co się stanie

Własna funkcja wykonuje callback

Callback nie musi działać później. Ta funkcja wywołuje go natychmiast w zwykłej pętli. Samo użycie strzałki nie sprawia, że kod jest asynchroniczny.

src/index.ts — osobny przykład
function powtorz(ile: number, akcja: (numer: number) => void): void {
  for (let i = 1; i <= ile; i++) { akcja(i); }
}
powtorz(3, numer => console.log("Runda " + numer));
wynik w konsoli
Runda 1
Runda 2
Runda 3

Funkcje strzałkowe nie mają własnego this: przejmują je z otoczenia. Wrócimy do tego przy klasach. Na razie ważniejsze jest odróżnienie funkcji od wyniku jej wywołania.

CZĘSTE BŁĘDY

Gdy wynik zaskakuje

PomyłkaCo się dzieje i jak to poprawić
Przekazujesz podwoj(5) zamiast podwojPierwszy zapis daje liczbę. Callbackiem ma być funkcja, którą odbiorca sam wywoła.
forEach zamiast mapforEach zwraca undefined. Do zbudowania nowej tablicy użyj map.
Próba break w callbacku forEachbreak nie przerywa wywołującej pętli przez granicę funkcji. Wybierz for…of, jeśli potrzebujesz przerwania.
ZADANIA

Teraz Twoja kolej

Rozwiązanie jest jedną z możliwości. Zanim je odkryjesz, uruchom własną wersję dla podanych danych i przynajmniej jednego przypadku brzegowego.

ZAD 1Przelicz minuty★☆☆

Napisz strzałkę zamieniającą minuty na sekundy. Sprawdź 0 i 3.

Mała podpowiedź

Jedno wyrażenie nie potrzebuje klamer.

Przykładowe rozwiązanie z wyjaśnieniem
src/index.ts — osobny przykład
const sekundy = (minuty: number): number => minuty * 60;
console.log(sekundy(0), sekundy(3));
wynik w konsoli
0 180

Typ wyniku zapisujemy dla czytelności, mimo że kompilator potrafi go ustalić.

ZAD 2Głośne nicki★★☆

Z tablicy [„ala”, „pixel”] utwórz nową tablicę wielkich liter. Wypisz wynik i oryginał.

Mała podpowiedź

map zbiera tekst zwrócony przez toUpperCase.

Przykładowe rozwiązanie z wyjaśnieniem
src/index.ts — osobny przykład
const nicki = ["ala", "pixel"];
const glosne = nicki.map(nick => nick.toUpperCase());
console.log(glosne.join(", "));
console.log(nicki.join(", "));
wynik w konsoli
ALA, PIXEL
ala, pixel

Teksty są niemutowalne; wynik metody trzeba wykorzystać.

ZAD 3Lista zaproszonych★★☆

Wybierz wyniki co najmniej 50 z [49, 50, 80], potem dopisz „pkt” do każdego. Zachowaj kolejność.

Mała podpowiedź

Najpierw filter, potem map.

Przykładowe rozwiązanie z wyjaśnieniem
src/index.ts — osobny przykład
const zaproszeni = [49, 50, 80]
  .filter(p => p >= 50)
  .map(p => p + " pkt");
console.log(zaproszeni.join(" | "));
wynik w konsoli
50 pkt | 80 pkt

Granica 50 zostaje, ponieważ użyliśmy >=, a nie >.

ZAD 4Wymienne zasady★★★

Napisz oblicz(a, b, dzialanie). Przetestuj dodawanie i mnożenie dla 3 i 4 bez if i switch w oblicz.

Mała podpowiedź

Parametr działania przyjmuje dwie liczby i oddaje jedną.

Przykładowe rozwiązanie z wyjaśnieniem
src/index.ts — osobny przykład
function oblicz(a: number, b: number, dzialanie: (x: number, y: number) => number): number {
  return dzialanie(a, b);
}
console.log(oblicz(3, 4, (x, y) => x + y));
console.log(oblicz(3, 4, (x, y) => x * y));
wynik w konsoli
7
12

Funkcja oblicz nie zna zasad działania: dostaje je od wywołującego.

PODSUMOWANIE

Co zabierasz do następnej lekcji

  • Krótka strzałka zwraca wyrażenie; blok wymaga return.
  • Callback to przekazana funkcja, nie wynik wywołania.
  • map przekształca, filter wybiera, forEach wykonuje czynność.