Higher-Order Functions

Функция высшего порядка (HOF) — функция, которая принимает другую функцию как аргумент и/или возвращает функцию как результат. Основа функционального программирования и большинства паттернов в JS.

Что это / Зачем

  • HOF — фундамент функционального стиля
  • Позволяет параметризовать поведение, а не только данные
  • map, filter, reduce, addEventListener — все HOF
  • Замыкания, каррирование, декораторы, мемоизация — всё построено на HOF

Четыре вида использования HOF

// 1. Принимает функцию
arr.forEach((x) => console.log(x));

// 2. Возвращает функцию
const multiplier = (n) => (x) => x * n;

// 3. И принимает, и возвращает (декораторы)
const withLog = (fn) => (...args) => {
  console.log('call:', args);
  return fn(...args);
};

// 4. Каррированная функция — цепочка HOF
const curried = (a) => (b) => (c) => a + b + c;

Примеры

Математическая HOF: inverse

const inverse = (fn) => (x) => 1 / fn(x);
const sqrtInv = inverse(Math.sqrt); // 1 / Math.sqrt(x)

Каррирование цепочкой лямбд

const add = (a) => (b) => a + b;
add(2)(3); // 5

// На каждом вызове — новое замыкание
const add2 = add(2);
const add3 = add(3);
// add2 и add3 — разные функции с разным замыканием

Замыкание на выходе функции

function makeCounter() {
  let count = 0;
  return  => ++count;
}
const c1 = makeCounter; // отдельное замыкание
const c2 = makeCounter; // другое замыкание
c1; c1; // 2
c2;       // 1 (независимо)

Декоратор

const logable = (fn) => (...args) => {
  console.log(`${fn.name}(${args.join(',')})`);
  const result = fn(...args);
  console.log('result:', result);
  return result;
};

Каррированный таймер

const timeout = (msec) => (callback) => setTimeout(callback, msec);
const after2s = timeout(2000);
after2s( => console.log('Прошло 2 секунды'));

Контракт error-first callback

// Node-style: первый аргумент — ошибка, дальше результат
fs.readFile('file.txt', 'utf8', (err, data) => {
  if (err) return console.error(err);
  console.log(data);
});
  • Универсальный контракт для асинхронных HOF
  • err всегда первый — проще проверка
  • setTimeout НЕ следует контракту — нужен wrapper

Wrapper для setTimeout под контракт

const timeout = (msec, callback) =>
  setTimeout( => callback(null), msec); // null = no error

Ключевые правила

  • HOF — это просто функции, использующие свойство «функция как объект первого класса»
  • Возвращаемая функция замыкает контекст внешней
  • Каждый вызов HOF создаёт новое замыкание
  • Каррирование = частный случай HOF (цепочка возвращаемых функций)

Подводные камни

  • Создание функции внутри HOF на каждом вызове — расходы на память и оптимизатор
  • Цепочка then Promise — HOF; ошибки можно «потерять» без .catch
  • Listener (через subscribe) ≠ callback — слушатель срабатывает многократно
  • В синхронных HOF (map) callback срабатывает раньше return HOF — это часто путает

🎓 Источник: Higher-order functions, callbacks, and events

  • 📅 2018-10-11 · YouTube · ID: 1vqATwbGHnc
  • Тезисы:
    • HOF — фундамент почти всех паттернов JS
    • 4 вида использования: принимает, возвращает, и то и другое, каррирование
    • Чистая HOF не имеет побочных эффектов — обернуть «грязную» можно в HOF-декоратор
    • Listener против callback: первый срабатывает многократно, второй — один раз
    • ООП можно собрать без class — через сумматор-замыкание и shared методы
  • Цитата: «Функция высшего порядка — это функция, у которой минимум один аргумент — это другая функция, либо она возвращает другую функцию.»

См. также