Урок по треку JavaScript 6 мин

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

Функции как значения, композиция, каррирование, мемоизация, чистота.

В JavaScript функции это значения: их кладут в переменные, передают аргументами и возвращают из других функций. На этом держатся методы массивов, обработчики событий, middleware, хуки React. Функция высшего порядка (higher-order function, HOF) принимает функцию или возвращает функцию.

Способы объявить функцию

function declared(a, b) { return a + b; }   // объявление: поднимается целиком
const expr = function (a, b) { return a + b; }; // выражение: доступно после строки
const arrow = (a, b) => a + b;              // стрелка: короткая, без своего this

Отличия стрелок: нет своих this, arguments, super, нельзя вызвать через new, нет свойства prototype. Для методов объектов и конструкторов они не подходят, для колбэков идеальны.

Параметры

  • Значения по умолчанию срабатывают только для undefined, не для null: f(null) оставит null.
  • Rest-параметры (...args) собирают остаток в настоящий массив. Старый arguments это массивоподобный объект, и в стрелках его нет.
  • fn.length это число параметров до первого с дефолтом или rest. Им пользуются реализации curry.
  • Аргументы передаются по значению, но для объектов значение это ссылка: мутация объекта видна снаружи, переприсваивание параметра нет.

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

map, filter, reduce, some, every, find, sort, setTimeout, addEventListener, Promise.then принимают функции. Это делает код декларативным: вы говорите «что», а не «как».

const total = orders
  .filter((o) => o.paid)
  .map((o) => o.sum)
  .reduce((acc, s) => acc + s, 0);

Возврат функции: фабрики и замыкания

const makeMultiplier = (k) => (x) => x * k;
const double = makeMultiplier(2);
double(21); // 42

Композиция

Композиция склеивает функции в конвейер: результат одной идёт на вход следующей.

const pipe = (...fns) => (x) => fns.reduce((v, f) => f(v), x);
const slugify = pipe(
  (s) => s.trim(),
  (s) => s.toLowerCase(),
  (s) => s.replace(/\s+/g, '-'),
);
slugify('  Hello World '); // 'hello-world'

pipe применяет слева направо, compose справа налево.

Каррирование и частичное применение

Каррирование превращает f(a, b, c) в f(a)(b)(c). Частичное применение фиксирует часть аргументов и возвращает функцию от остальных: add.bind(null, 5) или (b) => add(5, b).

function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) return fn.apply(this, args);
    return (...more) => curried.apply(this, [...args, ...more]);
  };
}
const sum3 = curry((a, b, c) => a + b + c);
sum3(1)(2)(3); // 6
sum3(1, 2)(3); // 6

Чистые функции

Чистая функция при одинаковых аргументах всегда возвращает одинаковый результат и не имеет побочных эффектов: не меняет внешние переменные и аргументы, не ходит в сеть, не пишет в DOM. Такие функции легко тестировать, кешировать и вызывать повторно. Компоненты React и редьюсеры должны быть чистыми.

Мемоизация

Мемоизация кеширует результат по аргументам. Работает только для чистых функций.

function memoize(fn) {
  const cache = new Map();
  return (arg) => {
    if (!cache.has(arg)) cache.set(arg, fn(arg));
    return cache.get(arg);
  };
}

Нюансы: ключ по нескольким аргументам надо строить (например, вложенные Map), объекты сравниваются по ссылке, кеш растёт бесконечно без ограничения.

debounce и throttle

Две классические HOF для частых событий:

  • debounce вызывает функцию, когда события прекратились на заданное время (поиск при вводе);
  • throttle вызывает не чаще одного раза за интервал (scroll, resize).

Частые ошибки

  • ['1', '2', '3'].map(parseInt) даёт [1, NaN, NaN]: map передаёт индекс вторым аргументом, а parseInt считает его основанием.
  • Передать obj.method как колбэк и потерять this.
  • Мемоизировать нечистую функцию.
  • Думать, что дефолт сработает для null.

Что спросят на собесе

  • Что такое функция высшего порядка, примеры из стандартной библиотеки.
  • Чем каррирование отличается от частичного применения, напишите curry.
  • Что такое чистая функция и зачем она нужна.
  • Напишите memoize, compose или pipe.
  • Чем стрелочная функция отличается от обычной.

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.