Попрощайтесь с вложенностью функций в JavaScript
Репозиторий давно не обновлялся
Последнее обновление было 2 года назад.
Знакома ситуация? Вы берете данные, применяете одну функцию, к результату — другую, потом третью... и вот уже ваш код напоминает матрешку или пирамиду из скобок. Читать такое — мука, а вносить правки — еще хуже.
// Узнали? Заболели?
three(two(one(value)));
С другой стороны, есть элегантные цепочки методов, как в jQuery или Lodash:
value.one().two().three();
Красиво, линейно, понятно. Но есть проблема: так можно делать, только если one, two и three — это методы объекта value. А что, если это обычные функции? Или вам нужно смешать вызовы методов с арифметикой, созданием объектов и await?
Именно эту головную боль и призван решить оператор конвейера (|> или pipe operator) — одно из самых ожидаемых нововведений в JavaScript, которое сейчас находится на 2 стадии рассмотрения в комитете TC39. Давайте разберемся, как он работает и почему за ним стоит следить.
Что такое Pipe Operator и зачем он нужен?
Если коротко, pipe-оператор |> — это синтаксический сахар, который позволяет передавать результат одного выражения в качестве аргумента для следующего, делая код более линейным и читаемым.
Он берет лучшее от двух миров:
- Универсальность вложенных вызовов: работает с любыми функциями, методами, операциями.
- Читаемость цепочек методов: код выполняется слева направо, как мы привыкли читать.
Давайте посмотрим на реальный пример из кодовой базы React. Вот так выглядит код без оператора конвейера:
console.log(
chalk.dim(
`$ ${Object.keys(envars)
.map(envar =>
`${envar}=${envars[envar]}`)
.join(' ')
}`,
'node',
args.join(' ')
)
);
Чтобы понять, что здесь происходит, приходится буквально "нырять" вглубь скобок, находить самую вложенную часть (envars) и мысленно раскручивать клубок обратно.
А теперь магия. Вот как этот же код преображается с использованием pipe-оператора:
Object.keys(envars)
.map(envar => `${envar}=${envars[envar]}`)
.join(' ')
|> `$ ${%}`
|> chalk.dim(%, 'node', args.join(' '))
|> console.log(%);
Разница колоссальная! Мы сразу видим исходные данные и последовательно, шаг за шагом, следим за их трансформацией. Код читается как рассказ.
Как это работает? Ключевые детали
Основная идея проста: значение |> операция. Выражение слева от |> вычисляется, и его результат "вставляется" в выражение справа.
Специальный токен %
Куда именно вставляется результат? Для этого используется специальный токен-плейсхолдер — %. Он указывает место, куда подставится значение из предыдущего шага.
Интересно, что % — это временный вариант. Комитет еще обсуждает, какой символ будет финальным, но пока остановились на этом.
Гибкость — его второе имя
Самое крутое в этом подходе (который называется "Hack-style pipes") — его невероятная гибкость. Плейсхолдер % можно использовать практически где угодно, где может стоять переменная:
- Вызов обычной функции:
value |> myFunc(%) - Вызов с несколькими аргументами:
value |> myFunc(10, %, 'hello') - Вызов метода:
value |> %.myMethod() - Арифметика:
value |> % + 10 - Создание массива:
value |> [%, 'anotherValue'] - Создание объекта:
value |> { data: % } - Шаблонные строки:
value |> `Результат: ${%}` - Работа с
await:promise |> await %
Эта универсальность и делает оператор таким мощным. Вам не нужно оборачивать все в функции — вы просто пишете код, как привыкли, но в линейном стиле.
Почему не просто временные переменные?
Скептик скажет: "Зачем новый оператор? Я могу просто разбить все на переменные!"
const keys = Object.keys(envars);
const mapped = keys.map(envar => `${envar}=${envars[envar]}`);
const joined = mapped.join(' ');
const template = `$ ${joined}`;
const dimmed = chalk.dim(template, 'node', args.join(' '));
console.log(dimmed);
Да, так можно. Но это порождает другие проблемы:
- Много "мусора". Появляется куча одноразовых переменных, которые загромождают область видимости и усложняют чтение. Не зря говорят, что одно из самых сложных дел в программировании — давать хорошие имена.
- Утомительно. Писать
const name = ...для каждого мелкого шага попросту долго и нудно. - Не всегда возможно. Pipe-оператор — это выражение (expression), а не инструкция (statement). Это значит, что его результат можно сразу вернуть из функции или использовать, например, в JSX, чего не сделаешь с последовательностью
const.
Битва пайплайнов: Hack против F#
Важно знать, что путь оператора в стандарт был тернист. Долгое время существовало два конкурирующих предложения:
- Hack pipes (тот, что побеждает):
value |> func(%). Гибкий, универсальный, но требует явно указывать вызов(%)даже для функций с одним аргументом. - F# pipes:
value |> func. Очень лаконичен для унарных функций (с одним аргументом). Но для всего остального (методы, арифметика, функции с несколькими аргументами) требует обертки в стрелочную функцию:value |> (x => x.method()),value |> (x => x + 10).
Комитет TC39 дважды отклонял F#-стиль. Основные причины — он менее универсален для типичного JavaScript-кода, где вызовы методов и функций с несколькими аргументами встречаются чаще, чем унарные. К тому же, F#-стиль мог приводить к трудноуловимым ошибкам во время выполнения, если справа оказывалось не то, что ожидалось.
В итоге выбор пал на Hack-стиль как на более прагматичный и безопасный вариант, который лучше вписывается в существующую экосистему языка.
Что в итоге?
Оператор конвейера (|>) — это не просто синтаксическая безделушка. Это мощный инструмент для написания более чистого, понятного и поддерживаемого кода. Он устраняет "лесенки" из вложенных вызовов и позволяет мыслить в терминах потока данных: взяли данные, сделали раз, сделали два, получили результат.
Сейчас предложение находится на стадии 2, а это значит, что его концепция уже утверждена, и идет активная работа над деталями спецификации. Финальный синтаксис еще может немного измениться, но общая идея останется прежней.
Хотите попробовать уже сегодня? Вы можете! Оператор доступен через плагин для Babel: @babel/plugin-proposal-pipeline-operator.
Это отличная возможность заглянуть в будущее JavaScript и начать писать более выразительный код прямо сейчас. Попробуйте рефакторить свой самый запутанный кусок кода с его помощью — результат вас приятно удивит.
