«Front-end Advanced» полностью понимает состав функций

JavaScript
«Front-end Advanced» полностью понимает состав функций

Чем больше вы знаете, тем больше вы не знаете
点赞Посмотри еще раз, аромат остался в руке, и слава

введение

Композиция функций называется композицией в функциональном программировании, и мы поймем концепцию композиции и изучим множество примеров. затем создайте свой собственныйcomposeфункция.

Концепция комбинации очень интуитивно понятна, а не функциональное программирование, а можно увидеть в нашей жизни или в предельном развитии.

Например, наше популярное SPA (одностраничное приложение) будет иметь концепцию компонентов.Почему у нас есть концепция компонентов, потому что ее цель состоит в том, чтобы позволить вам абстрагировать некоторые общие функции или комбинации элементов в повторно используемые компоненты, даже если это не так. Универсальный При создании сложной страницы вы также можете разделить ее на компоненты с простыми функциями, а затем объединить их в страницы, отвечающие вашим различным потребностям.

По сути, функциональная композиция похожа на функциональную композицию — это процесс объединения простых задач, которые были декомпозированы на сложные задачи.

что такое комбинация

Давайте рассмотрим команду, обычно используемую в системах Linux.ps -ef | grep node

Целью этой команды является отображение связанных с узлом процессов в системе, среди которыхps -efэто полный формат, показывающий все процессы,grep nodeзаключается в фильтрации содержимого, связанного с узлом,|это функция, которая отправляет выходные данные функции слева в качестве входных данных для функции справа.

Этот пример может быть тривиальным, но он передает идею:

Вывод каждой программы может быть вводом другой программы, которая еще не известна.

Согласно нашему пониманию композиции, теперь предполагается, что мы имеемcomposeФункция может выполнять следующие функции:

function compose(...fns){
    //忽略
}
// compose(f,g)(x) === f(g(x))
// compose(f,g,m)(x) === f(g(m(x)))
// compose(f,g,m)(x) === f(g(m(x)))
// compose(f,g,m,n)(x) === f(g(m(n(x))))
//···

Мы видим, чтоcomposeФункция получит несколько функций в качестве параметров, и вывод каждой функции после выполнения используется в качестве вывода следующей функции, пока вывод последней функции не будет использован в качестве конечного результата.

Применить функцию создания

в создании и совершенствовании собственногоcomposeПеред функцией давайте научимся ее применятьcomposeфункция.

Предположим, есть необходимость округлить заданное число, которое является символьным типом.

Регулярная реализация:

let n = '3.56';
let data = parseFloat(n);
let result = Math.round(data); // =>4 最终结果

В этом коде вы можете увидетьparseFloatВывод функции передается в качестве входных данныхMath.roundфункция для получения конечного результата, которыйcomposeТипичные проблемы, которые могут решить функции.

использоватьcomposeПереписать функцию:

let n = '3.56';
let number = compose(Math.round,parseFloat);
let result = number(n); // =>4 最终结果

Ядро этого кода проходит черезcomposeБудуparseFloatа такжеMath.roundобъединить вместе, чтобы вернуть новую функциюnumber.

Этот процесс композиции является функциональной композицией! Мы объединяем две функции вместе, чтобы новая функция могла быть построена как раз вовремя!

В качестве другого примера предположим, что у нас есть две функции:

let splitIntoSpaces = str => str.split(' ');
let count = array => array.length;

Теперь я хочу создать новую функцию для подсчета количества слов в строке Это можно сделать легко:

let countWords = compose(count,splitIntoSpaces);

Назови это:

let result = countWords('hello your reading about composition'); // => 5

Полезность композиции в развитии

Предположим, у нас есть такое требование: дать вам строку, преобразовать строку в верхний регистр, а затем изменить порядок.

Наше общее мнение таково:

let str = 'jspool'

//先转成大写,然后逆序
function fn(str) {
    let upperStr = str.toUpperCase()
    return upperStr.split('').reverse().join('')
}

fn(str) // => "LOOPSJ"

Этот код работает нормально, но теперь требования были изменены, чтобы распаковать и упаковать каждый символ в массив после капитализации строки:

"jspool" => ["J","S","P","O","O","L"]

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

Принцип открытости-закрытости: объекты (классы, модули, функции и т. д.) в программном обеспечении должны быть открыты для расширения, но закрыты для модификации.

Итак, когда требования не изменились, а строки по-прежнему пишутся с заглавной буквы и наоборот, как использовать идею комбинирования для записи?

Исходное требование, мы можем достичь этого:

let str = 'jspool'

function stringToUpper(str) {
    return str.toUpperCase()
}

function stringReverse(str) {
    return str.split('').reverse().join('')
}

let toUpperAndReverse = compose(stringReverse, stringToUpper)
let result = toUpperAndReverse(str) // "LOOPSJ"

Затем, когда нам нужно изменить строку на верхний регистр и разбить ее на массив, нам вообще не нужно изменять ранее инкапсулированную функцию:

let str = 'jspool'

function stringToUpper(str) {
    return str.toUpperCase()
}

function stringReverse(str) {
    return str.split('').reverse().join('')
}

function stringToArray(str) {
    return str.split('')
}

let toUpperAndArray = compose(stringToArray, stringToUpper)
let result = toUpperAndArray(str) // => ["J","S","P","O","O","L"]

Видно, что при изменении требований мы не ломали ранее инкапсулированный код, а только добавляли функции, а потом функции рекомбинировали.

У кого-то могут возникнуть сомнения, когда код пишется комбинационным способом, когда меняются требования, код все равно модифицируется, это тоже не нарушение принципа открытости-закрытости? По сути, мы модифицируем логический код вызова, а не модифицируем инкапсулированный и абстрагированный код, и этот метод написания также отстаивается по принципу открыто-закрыто.

Предположим, что мы снова изменили требования.Текущее требование состоит в том, чтобы перехватить первые 3 символа после преобразования строки в верхний регистр, а затем преобразовать ее в массив, тогда мы можем реализовать это следующим образом:

let str = 'jspool'

function stringToUpper(str) {
    return str.toUpperCase()
}

function stringReverse(str) {
    return str.split('').reverse().join('')
}

function getThreeCharacters(str){
    return str.substring(0,3)
}

function stringToArray(str) {
    return str.split('')
}

let toUpperAndGetThreeAndArray = compose(stringToArray, getThreeCharacters,stringToUpper)
let result = toUpperAndGetThreeAndArray(str) // => ["J","S","P"]

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

достичь комбинации

оглядыватьсяcomposeЧто именно делает функция:

// compose(f,g)(x) === f(g(x))
// compose(f,g,m)(x) === f(g(m(x)))
// compose(f,g,m)(x) === f(g(m(x)))
// compose(f,g,m,n)(x) === f(g(m(n(x))))
//···

В двух словах, он принимает несколько функций в качестве параметров и возвращает новую функцию. Когда новая функция выполняется, согласно由右向左Порядок оформления поступающихcomposeФункции в , результат выполнения каждой функции используется в качестве ввода следующей функции, пока вывод последней функции не используется в качестве окончательного результата вывода.

еслиcomposeКоличество функций, полученных функцией, фиксировано, поэтому реализация очень проста и простая для понимания.

Принимает только два параметра:

function compose(f,g){
    return function(x){
        return f(g(x));
    }
}

Принимаются только три параметра:

function compose(f,g,m){
    return function(x){
        return f(g(m(x)));
    }
}

В приведенном выше коде нет проблем, но мы должны учитывать следующее.composeКоличество принимаемых параметров неизвестно, рассматриваем использование остальных параметров для получения:

function compose(...fns){
    return function(x){
        //···
    }
}

Сейчасcomposeполученные параметрыfnsявляется массивом, то теперь возникает вопрос, как объединить функции в массиве.从右至左Выполнять последовательно.

Мы выбираем массив изreduceRightфункция для реализации:

function compose(...fns){
    return function(x){
        return fns.reduceRight(function(arg,fn){
            return fn(arg);
        },x)
    }
}

Так мы достиглиcomposeфункция~

Реализовать конвейер

composeПоток данных从右至左, потому что самая правая функция выполняется первой, а самая левая функция выполняется последней!

но некоторым нравится从左至右Метод выполнения , то есть самая левая функция выполняется первой, а самая правая функция выполняется последней!

Процесс обработки потока данных слева направо называется конвейером!

管道(pipeline)осуществление того жеcomposeРеализация очень похожа, потому что единственная разница между ними заключается в направлении потока данных.

В сравненииcomposeРеализация функции требует толькоreduceRightзаменитьreduceТолько что:

function pipe(...fns){
    return function(x){
        return fns.reduce(function(arg,fn){
            return fn(arg);
        },x)
    }
}

а также组合Некоторые люди предпочитают管道. Это просто личное предпочтение и не имеет ничего общего с базовой реализацией. Дело в томpipeа такжеcomposeДелать то же самое — это просто другой выпуск потока данных! Мы можем использовать в кодеpipeилиcompose, но не оба, так как это может вызвать путаницу среди членов команды. Если вы собираетесь его использовать, придерживайтесь только одной комбинации стилей.

Рекомендуемая серия статей

Ссылаться на

напиши в конце

  • Если в статье есть ошибки, исправьте их в комментариях, если статья вам поможет, добро пожаловать点赞а также关注
  • Эта статья была впервые опубликована одновременно сgithub, доступны наgithubНайдите больше отличных статей вWatch & Star ★
  • Для последующих статей см.:строить планы

Добро пожаловать в публичный аккаунт WeChat【前端小黑屋】, 1–3 высококачественные высококачественные статьи публикуются каждую неделю, чтобы помочь вам в продвижении вперед.