Структурируй код с помощью каррирования, композиции функций и неизменяемых данных, чтобы выиграть в предсказуемости и тестируемости.
Открыть этот урок в KodokonJavaScript трактует функции как значения первого класса, и это открывает дорогу функциональным паттернам. Каррирование превращает функцию от n аргументов в цепочку функций от одного аргумента: f(a, b, c) становится f(a)(b)(c). Не путай его с частичным применением, которое фиксирует часть аргументов за один шаг (fn.bind(null, a)). Каррирование особенно хорошо, когда нужно создавать специализированные функции из общих.
const curry = (fn) => {
const arity = fn.length;
return function curried(...args) {
if (args.length >= arity) return fn(...args);
return (...rest) => curried(...args, ...rest);
};
};
const add3 = (a, b, c) => a + b + c;
const addCurried = curry(add3);
console.log(addCurried(1)(2)(3)); // 6
console.log(addCurried(1, 2)(3)); // 6Тонкость спецификации: fn.length считает только параметры, стоящие перед первым параметром со значением по умолчанию или rest-параметром. У (a, b = 1) => {} длина равна 1, у (...args) => {} - 0. Поэтому в curry, основанный на арности, нужно передавать функции с простыми параметрами. Композиция - следующий шаг: сборка маленьких чистых функций в читаемые конвейеры.
const pipe = (...fns) => (input) =>
fns.reduce((acc, fn) => fn(acc), input);
const trim = (s) => s.trim();
const lower = (s) => s.toLowerCase();
const dashes = (s) => s.replace(/\s+/g, "-");
const toSlug = pipe(trim, lower, dashes);
console.log(toSlug(" Hello World "));
// "hello-world"pipe(f, g, h)(x) вычисляет h(g(f(x))): это читается в порядке выполнения. Вариант compose применяет функции справа налево, как в математической записи. Эти конвейеры ценны, только если функции чистые - без побочных эффектов, без скрытых зависимостей, - а это требует неизменяемых данных: вместо того чтобы менять объект, ты создаёшь его новую версию, и тогда равенства ссылок достаточно, чтобы обнаружить изменение (сердце согласования в React).
const state = Object.freeze({
user: { name: "Ada" },
tags: ["js"]
});
const next = {
...state,
tags: [...state.tags, "fp"]
};
const deepCopy = structuredClone(state);
deepCopy.user.name = "Grace"; // independent copybind, каррирование - только со стрелочными функциямиpipe(f, g, h)(x)?const s = Object.freeze({ a: { b: 1 } }) сработает ли присваивание s.a.b = 2?s.a не заморожен