
В этом видео мы с вами разберем такую тему, как композиция в javascript. Этот вопрос достаточно часто задают на собеседовании.
Что такое композиция? Это процесс комбинирования двух или больше функций, для создания новой функции.
То есть код будет выглядеть вот так
f(g(x))
То есть сначала у нас есть какая-то переменная x, потом мы вызываем функцию g на x, а потом на результат g вызываем f.
Давайте попробуем на практике. Напишем функцию, которая превращает строку в slug, который мы можем использовать в качестве ссылки поста, например.
То есть код
const slug = toSlug('This is composition')
должен вернуть нам строку
this-is-composition
На чистом js мы можем написать как-то так
const toSlug = input => {
const words = input.split(' ')
const lowercasedWords = words.map(word => word.toLowerCase())
const slug = lowercasedWords.join('-')
const encodedSlug = encodeURIComponent(slug)
return encodedSlug
}
console.log(toSlug('This is composition'))
Это достаточно неплохой и читабельный код. Но в нем есть одна проблема. В процессе создания функции toSlug, мы должны были, для читабельности, создать 4 дополнительные переменные. Хотя, по факту, нас интересует только input и output, и переменные мы потом не переиспользуем. Да и код внутри переменных не особо сложный.
Давайте попробуем написать тоже самое, но без дополнительных переменных.
const toSlug = input => encodeURIComponent(
input.split(' ')
.map(str => str.toLowerCase())
.join('-')
)
Вышло неплохо, но слабо читабельно, так как внутри encodeURIComponent идет большая конструкция.
Давайте попробуем из этого сделать вложенность композиции. Для этого, нам нужно, чтобы каждая функция, которую мы применяем могла каррироваться.
const toSlug = input =>
R.split(' ')(input)
Это разобьет нам input на слова. Мы можем так писать, потому что все методы Ramda каррируются.
Дальше нам нужно каждое слово в массиве перевести в нижний регистр.
const toSlug = input =>
R.map(R.toLower)(
R.split(' ')(input)
)
То есть результат split - это массив. И мы на этот массив применяем R.map, передавая ему R.toLower. R.toLower - это аналог toLowerCase из javascript, только каррированый. Как вы видите, мы с вами вкладываем функцию в функцию, каждый раз передавая результат дальше.
Теперь добавим join.
const toSlug = input =>
R.join('-')(
R.map(R.toLower)(
R.split(' ')(input)
)
)
И теперь вызовем encodeURIComponent на результат
const toSlug = input =>
encodeURIComponent(
R.join('-')(
R.map(R.toLower)(
R.split(' ')(input)
)
)
)
Если мы посмотрим в браузер, то наш код работает так же, как и раньше.
Этот код не выглядит очень лаконичным из-за большого количества вложенных функций, но зато он трактуется однозначно. Мы видим, что с самого низа у нас есть input и потом вызывается все новая и новая функция, пока мы не дойдем до самой верхней функции и не вернем результат.
Также, как вы видите, мы постоянно передаем только результат предыдущей функции в следующую функцию.
Единственное, что сейчас хотелось бы сделать, это избавится от вложенности функций.
В Ramda есть функция compose, которая позволяет делать композиции в более читабельном варианте. В нее можно передать цепочку функций, которые по очереди применятся на аргумент и будут передавать результат каждой функции дальше.
Давайте попробуем.
const toSlug = input => R.compose(
R.split(' ')
)(input)
Как мы видим, код работает также, как и до этого. Давайте добавим остальные функции.
const toSlug = input => R.compose(
encodeURIComponent,
R.join('-'),
R.map(R.toLower),
R.split(' ')
)(input)
Как это работает? Мы применяем R.compose на input. Он идет последним аргументом и дальше вызываются все функции по очереди с права на лево. Так как все функции каррируются, то результат в следующую функцию передается автоматически.
Мы избавились от вложенности и обязательного прокидывания аргументов. Также наша функция toSlug, теперь избавилась от 4х ненужных переменных.
Единственное, что нужно помнить, это то, что R.compose тоже каррируется, поэтому этот код можно еще упростить, убрав аргумент.
const toSlug = R.compose(
encodeURIComponent,
R.join('-'),
R.map(R.toLower),
R.split(' ')
)
Если у вас возникли какие-то вопросы или комментарии, пишите их прямо под этим видео.