Domů O mně Služby Portfolio Učím Blog Kontakt
← Zpět na učím

03.16 Higher-order Functions

Výuka

Proč Higher-order Functions?

„Existují dva způsoby konstruování softwarového návrhu: jeden je udělat ho natolik jednoduchý, že zřejmě nemá žádné nedostatky, a druhý je udělat ho natolik složitý, že nemá žádné zřejmé nedostatky." — C.A.R. Hoare

Higher-order functions (funkce vyššího řádu) jsou funkce, které:

  • Přijímají jiné funkce jako argumenty, nebo
  • Vrací funkce jako výsledek

Proč to potřebujeme?

  • Abstrakce akcí - Ne jen abstrakce hodnot, ale abstrakce celých akcí
  • Znovupoužitelnost - Napsat obecnou logiku jednou, použít s různými akcemi
  • Funkcionální programování - Pracovat s funkcemi jako s "občany první třídy"
  • Čistší kód - Méně opakování, více expresivity

Představ si to jako: Tovární linku. Místo abys měl speciální stroj na každý produkt, máš univerzální stroj, kterému řekneš "jak" má pracovat. Můžeš mu předat různé "návody" (funkce) a stejný stroj vytvoří různé produkty.

Jak to funguje?

1. Funkce je hodnota (jako číslo nebo text)
2. Funkce může přijmout jinou funkci jako parametr
3. Funkce může vytvořit a vrátit novou funkci
4. Výsledná funkce "pamatuje" prostředí, ve kterém vznikla (closure)

Klíčové koncepty

  • Higher-order function - funkce pracující s jinými funkcemi
  • First-class functions - funkce jsou hodnoty jako jakékoli jiné
  • Abstrakce - zabalit složitost do pojmenovaného konceptu
  • Funkce vracející funkci - factory pattern pro vytváření specializovaných funkcí
  • Built-in HOF - map, filter, reduce, forEach - vestavěné higher-order funkce na polích

JavaScript

Příklad 1: Funkce přijímající funkci (nejjednodušší HOF)

function repeat(n, action) {
  for (let i = 0; i < n; i++) {
    action(i);
  }
}

repeat(3, console.log);
// → 0
// → 1
// → 2

Co se stalo?

  1. repeat je higher-order function - přijímá funkci action jako parametr
  2. Zavoláme repeat(3, console.log) - předali jsme funkci console.log
  3. repeat zavolá console.log třikrát s hodnotami 0, 1, 2

Proč je to užitečné? Abstrahovali jsme "opakování N-krát" - obsah akce je flexibilní!

Příklad 2: Funkce vracející funkci

function greaterThan(n) {
  return function(m) {
    return m > n;
  };
}

const greaterThan10 = greaterThan(10);
console.log(greaterThan10(11));  // → true
console.log(greaterThan10(9));   // → false

Co se stalo?

  1. greaterThan(10) vytvoří a vrátí novou funkci
  2. Tato funkce "pamatuje" n = 10 (closure!)
  3. greaterThan10 je funkce, která testuje, zda je číslo > 10
  4. Můžeme vytvořit různé verze: greaterThan5, greaterThan100, atd.

Příklad 3: Praktický příklad - noisy (logging wrapper)

function noisy(f) {
  return function(...args) {
    console.log("Volání s argumenty:", args);
    const result = f(...args);
    console.log("Vrácená hodnota:", result);
    return result;
  };
}

const noisyMin = noisy(Math.min);
noisyMin(3, 2, 1);
// → Volání s argumenty: [3, 2, 1]
// → Vrácená hodnota: 1

Co se stalo?

  • noisy je wrapper - obaluje jinou funkci a přidává logování
  • Vrací novou funkci, která:
    1. Zaloguje argumenty
    2. Zavolá původní funkci
    3. Zaloguje výsledek
    4. Vrátí výsledek
  • Můžeš "obalit" jakoukoliv funkci!

Příklad 4: Array.map - transformace pole

const numbers = [1, 2, 3, 4];
const doubled = numbers.map(function(n) {
  return n * 2;
});

console.log(doubled);
// → [2, 4, 6, 8]

// Stejné s arrow funkcí (stručnější)
const tripled = numbers.map(n => n * 3);
console.log(tripled);
// → [3, 6, 9, 12]

Co se stalo?

  • map je vestavěná higher-order funkce na polích
  • Přijímá funkci, která transformuje každý prvek
  • Vytváří nové pole (nemění původní)
  • Je to čistá funkce - žádné vedlejší efekty

Příklad 5: Array.filter - filtrování pole

const numbers = [1, 2, 3, 4, 5, 6];
const evenNumbers = numbers.filter(function(n) {
  return n % 2 === 0;
});

console.log(evenNumbers);
// → [2, 4, 6]

// S arrow funkcí
const oddNumbers = numbers.filter(n => n % 2 === 1);
console.log(oddNumbers);
// → [1, 3, 5]

Co se stalo?

  • filter je další vestavěná higher-order funkce
  • Přijímá funkci (test), která vrací true/false
  • Vytváří nové pole obsahující pouze prvky, které prošly testem

Příklad 6: Kombinace map a filter (funkcionální pipeline)

const users = [
  { name: "Alice", age: 25 },
  { name: "Bob", age: 17 },
  { name: "Charlie", age: 30 },
  { name: "Diana", age: 16 }
];

// Získat jména dospělých uživatelů
const adultNames = users
  .filter(user => user.age >= 18)
  .map(user => user.name);

console.log(adultNames);
// → ["Alice", "Charlie"]

Co se stalo?

  1. filter vytvoří pole pouze s dospělými (věk >= 18)
  2. map transformuje pole objektů na pole jmen
  3. Výsledek: pole jmen dospělých uživatelů

Krása: Čitelný, deklarativní kód - říkáš "co chceš", ne "jak to udělat"


TypeScript

TypeScript přidává typové anotace pro higher-order funkce - můžeš specifikovat typy vstupních i výstupních funkcí.

Stejné příklady s typy

// Funkce přijímající funkci
function repeat(n: number, action: (i: number) => void): void {
  for (let i = 0; i < n; i++) {
    action(i);
  }
}

repeat(3, (i) => console.log(i));

// Funkce vracející funkci
function greaterThan(n: number): (m: number) => boolean {
  return (m: number) => m > n;
}

const greaterThan10 = greaterThan(10);
console.log(greaterThan10(11));  // → true

// Typovaný map
const numbers: number[] = [1, 2, 3, 4];
const doubled: number[] = numbers.map((n: number): number => n * 2);

// Typovaný filter
interface User {
  name: string;
  age: number;
}

const users: User[] = [
  { name: "Alice", age: 25 },
  { name: "Bob", age: 17 }
];

const adults: User[] = users.filter((user: User): boolean => user.age >= 18);
const adultNames: string[] = adults.map((user: User): string => user.name);

TypeScript přidává:

  • Kontrolu typů - editor zkontroluje, že funkce vrací správný typ
  • IntelliSense - automatické návrhy a nápověda pro parametry
  • Bezpečnost - nemůžeš omylem předat nekompatibilní funkci
  • Dokumentaci v kódu - typové signatury jsou živá dokumentace

Rozdíl JS vs TS

JavaScript:

  • Higher-order funkce fungují bez typů
  • Flexibilnější, ale méně bezpečné
  • Chyby zjistíš až při běhu programu
  • Můžeš omylem předat funkci s nesprávným podpisem

TypeScript:

  • Higher-order funkce mají jasné typové signatury
  • TypeScript zkontroluje, že funkce sedí
  • Chyby odhalíš už při psaní kódu
  • Editor ti pomůže s parametry a návratovými hodnotami
// JavaScript - projde, ale způsobí problém
function map(array, transform) {
  return array.map(transform);
}
const result = map([1, 2, 3], (x) => x.toUpperCase());  // Runtime error!

// TypeScript - editor tě upozorní
function map<T, U>(array: T[], transform: (item: T) => U): U[] {
  return array.map(transform);
}
const result = map([1, 2, 3], (x) => x.toUpperCase());
// ❌ Error: Property 'toUpperCase' does not exist on type 'number'

Tip

💡 Používej vestavěné HOF (map, filter, reduce) místo cyklů:

// ❌ Imperativní styl - jak to udělat (cyklus)
const numbers = [1, 2, 3, 4];
const doubled = [];
for (let i = 0; i < numbers.length; i++) {
  doubled.push(numbers[i] * 2);
}

// ✅ Deklarativní styl - co chceš (map)
const doubled = numbers.map(n => n * 2);

💡 Chainování (řetězení) HOF je čitelné:

// ✅ Čitelný funkcionální pipeline
const result = data
  .filter(x => x.active)
  .map(x => x.name)
  .sort();

// ⚠️ Pozor na výkon - každý krok vytvoří nové pole
// Pro velká data zvažte reduce nebo for-loop

💡 Arrow funkce jsou přirozenější pro HOF:

// ❌ Verbose - anonymní function
numbers.map(function(n) { return n * 2; });

// ✅ Stručné - arrow funkce
numbers.map(n => n * 2);

// ✅ Ještě stručnější - implicitní return
numbers.filter(n => n > 10).map(n => n * 2);

Kvíz

Co je výsledek tohoto kódu?

function makeMultiplier(factor) {
  return function(number) {
    return number * factor;
  };
}

const double = makeMultiplier(2);
const triple = makeMultiplier(3);

console.log(double(5) + triple(4));

- Chybný výpočet

- double(5) vrátí 5 * 2 = 10, triple(4) vrátí 4 * 3 = 12, celkem 10 + 12 = 22

- To je jen výsledek double(5)

- To je jen výsledek triple(4)

Důležité: Každá vrácená funkce "pamatuje" svůj vlastní factor díky closure!

Důležité: Každá vrácená funkce "pamatuje" svůj vlastní factor díky closure!

🎯 Závěrečný projekt

Po dokončení všech 8 dílů vytvoříte jednoduchou Todо aplikaci v čistém JavaScriptu. Naučíte se, jak aplikovat vše, co jste se naučili, na reálný projekt.

Zobrazit podrobnosti projektu →

Připraveni začít?

Zaregistrujte se a získejte přístup ke všem dílům tohoto seriálu

Kontaktujte mě

Informace o seriálu

Obtížnost

Délka

Cca 480 minut

Počet videí

8 videí + projekty

Certifikát

Po dokončení obdržíte certifikát


Lekce v této sekci


Struktura lekcí (souborový strom)

06. Typescript specifika
  • v přípravě
08. Moduly tridy
  • v přípravě
09. React zaklady
  • v přípravě
10. React hooks
  • v přípravě
12. Nextjs server
  • v přípravě
13. Databaze auth
  • v přípravě
14. Nextjs pokrocile
  • v přípravě