Композиция звучит сложнее, чем есть на самом деле. В обычной разработке это просто способ собрать более сложное поведение из нескольких маленьких шагов, где выход одной функции становится входом для следующей.
Без композиции такой код быстро превращается в длинные вложенные вызовы. Он еще работает, но читать и менять его становится неприятно: приходится распутывать порядок вычислений прямо в момент чтения.
Ниже — когда композиция делает JavaScript-код проще, почему порядок аргументов влияет на переиспользование и как отлаживать pipeline без потери читаемости.
Обновлено 17 марта 2026: переписаны примеры, добавлен разбор
pipeи практических правил для data-last функций.
Что такое композиция на простом примере
Начнем с трех маленьких функций:
const upperCase = (value) => value.toUpperCase();
const exclaim = (value) => `${value}!`;
const repeat = (value) => `${value} `.repeat(3).trim();Если применить их напрямую, получится вложенный вызов:
repeat(exclaim(upperCase("I love coding")));
// I LOVE CODING! I LOVE CODING! I LOVE CODING!Это и есть композиция, только записанная вручную. Проблема в том, что с ростом количества шагов вложенность начинает мешать.
compose и pipe
Чтобы упростить чтение, можно собрать функции в отдельную абстракцию.
const compose = (...fns) => (value) =>
fns.reduceRight((acc, fn) => fn(acc), value);
const emphasize = compose(repeat, exclaim, upperCase);
emphasize("I love coding");compose применяет функции справа налево. Если тебе удобнее читать pipeline слева направо, используй pipe.
const pipe = (...fns) => (value) =>
fns.reduce((acc, fn) => fn(acc), value);
const emphasize = pipe(upperCase, exclaim, repeat);Обе функции делают одно и то же. Разница только в порядке чтения. В JavaScript-командах pipe часто воспринимается проще, потому что ближе к тому, как мы обычно читаем шаги обработки данных.
Почему порядок аргументов так важен
Композиция особенно хорошо работает, когда функции ожидают данные последним аргументом. Тогда их легче частично применять и комбинировать.
Возьмем каррированную версию map:
const map = (fn) => (list) => list.map(fn);
const prop = (key) => (obj) => obj[key];
const getName = prop("name");
const getNames = map(getName);
getNames([
{ name: "Alex" },
{ name: "Julia" },
]); // ["Alex", "Julia"]Почему это удобно:
getNameможно переиспользовать в любом месте;getNamesуже готов к работе с любым массивом;- такие функции легко встраиваются в
pipeилиcompose.
Если же данные идут первым аргументом, переиспользование обычно становится хуже. Ты привязываешь функцию к конкретному значению слишком рано.
Как отлаживать композицию
У композиции есть обратная сторона: когда pipeline длинный, не всегда очевидно, на каком шаге испортилось значение.
Самый простой способ - вставить tap.
const tap = (label) => (value) => {
console.log(label, value);
return value;
};
const pipe = (...fns) => (value) =>
fns.reduce((acc, fn) => fn(acc), value);
const emphasize = pipe(
tap("input"),
upperCase,
tap("after upperCase"),
exclaim,
repeat,
);tap не меняет данные, а только показывает, что происходит на каждом этапе. Это особенно полезно в длинных pipeline и в асинхронных сценариях.
Когда композиция действительно окупается
Композиция хороша там, где:
- есть несколько маленьких преобразований данных;
- каждую функцию можно назвать отдельно;
- шаги хочется переиспользовать в разных местах;
- логика выглядит как явный pipeline.
Она хуже работает там, где:
- каждая функция живет только ради одного места;
- шаги слишком сильно завязаны на общий контекст;
- без перехода по определениям невозможно понять даже простое выражение.
То есть композиция должна сокращать когнитивную нагрузку, а не переносить ее в десяток маленьких файлов.
Композиция функций полезна тогда, когда помогает мыслить шагами, а не вложенностями. Если код естественно складывается в цепочку преобразований, compose или pipe делают его чище, переиспользуемее и удобнее для тестирования.
Хороший ориентир: начни с маленьких функций, которые делают одну вещь, и держи данные последним аргументом. Именно после этого композиция начинает приносить реальную пользу, а не просто красиво выглядеть.