Урок 10

Функции высшего порядка

Функция высшего порядка принимает функцию как аргумент или возвращает функцию.

Принимают функцию

Методы массивов:

[1, 2, 3].map((x) => x * 2);
[1, 2, 3].filter((x) => x > 1);
[1, 2, 3].reduce((acc, x) => acc + x, 0);

setTimeout, addEventListener:

setTimeout(() => console.log('позже'), 1000);
element.addEventListener('click', handler);

Возвращают функцию

function multiplier(factor) {
    return (n) => n * factor;
}

const double = multiplier(2);
double(5); // 10

Оба

function compose(...fns) {
    return (x) => fns.reduceRight((acc, fn) => fn(acc), x);
}

forEach

[1, 2, 3].forEach((item, index, array) => {
    console.log(item, index);
});

Нельзя прервать. Возвращает undefined.

map

const doubled = [1, 2, 3].map((x) => x * 2); // [2, 4, 6]

Длина результата = длина исходного.

filter

const evens = [1, 2, 3, 4].filter((x) => x % 2 === 0); // [2, 4]

reduce

const sum = [1, 2, 3].reduce((acc, x) => acc + x, 0); // 6

С объектом:

const counted = ['a', 'b', 'a'].reduce((acc, x) => {
    acc[x] = (acc[x] ?? 0) + 1;
    return acc;
}, {});

some и every

[1, 2, 3].some((x) => x > 2);   // true
[1, 2, 3].every((x) => x > 0);  // true

find и findIndex

[1, 2, 3].find((x) => x > 1);       // 2
[1, 2, 3].findIndex((x) => x > 1);  // 1

flatMap

[1, 2].flatMap((x) => [x, x * 2]); // [1, 2, 2, 4]

Эквивалент map + flat.

sort со сравнением

[3, 1, 2].sort((a, b) => a - b); // [1, 2, 3]

Паттерн: pipe

const pipe = (...fns) => (x) => fns.reduce((acc, fn) => fn(acc), x);

const process = pipe(
    (x) => x * 2,
    (x) => x + 1,
    (x) => x.toString(),
);

process(5); // '11'

Паттерн: compose

То же, но справа налево:

const compose = (...fns) => (x) => fns.reduceRight((acc, fn) => fn(acc), x);

Паттерн: debounce

function debounce(fn, delay) {
    let timer;

    return (...args) => {
        clearTimeout(timer);
        timer = setTimeout(() => fn(...args), delay);
    };
}

const search = debounce((query) => console.log(query), 500);

Паттерн: throttle

function throttle(fn, delay) {
    let last = 0;

    return (...args) => {
        const now = Date.now();
        if (now - last >= delay) {
            last = now;
            fn(...args);
        }
    };
}

Паттерн: once

function once(fn) {
    let called = false;
    let result;

    return (...args) => {
        if (!called) {
            called = true;
            result = fn(...args);
        }
        return result;
    };
}

Паттерн: retry

async function retry(fn, attempts = 3, delay = 1000) {
    for (let i = 0; i < attempts; i++) {
        try {
            return await fn();
        } catch (e) {
            if (i === attempts - 1) throw e;
            await new Promise((r) => setTimeout(r, delay));
        }
    }
}

Практика: массив → словарь

function indexBy(items, key) {
    return items.reduce((acc, item) => {
        acc[item[key]] = item;
        return acc;
    }, {});
}

Итоги

  • Функции высшего порядка принимают или возвращают функции
  • Методы массивов — базовый пример
  • pipe, compose — композиция
  • debounce, throttle, once — утилиты
  • Основа функционального стиля