Функциональное программирование, действительно ароматное

внешний интерфейс JavaScript функциональное программирование

Первый раз я столкнулся с функциональным программированием когда работал в Xiaomi.Тогда я читал разные композиционные коды, написанные начальником до этого, а потом какие-то инструментальные функции ramda.Это выглядело очень сложно, и тогда я попробовал я изо всех сил жалуюсь на функциональное программирование. Теперь я оглядываюсь на него. В то время я был очень недальновидным и просто хотел сказать: «Это действительно ароматно».

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

Сегодняшняя статья в основном знакомит с идеей функционального программирования.

  • Работает ли функциональное программирование?
  • Что такое функциональное программирование?
  • Преимущества функционального программирования.

Объектно-ориентированное программирование (ООП) упрощает понимание кода за счет инкапсуляции изменений. Функциональное программирование (FP) упрощает понимание кода за счет минимизации изменений. -- Майкл Фезерс (Twitter)

Как мы все знаем, JavaScript — это динамический язык с большим количеством общих состояний, и со временем код становится настолько сложным, что становится громоздким и трудным в обслуживании. Объектно-ориентированный дизайн может помочь нам решить эту проблему в определенной степени, но этого недостаточно.

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

  • Расширяемость. Нужно ли мне постоянно реорганизовывать код для поддержки дополнительных функций?
  • Простая модульность — если я изменю один файл, повлияет ли это на другой?
  • Возможность повторного использования — много ли дублированного кода?
  • Тестируемость. Беспокоит ли меня добавление модульных тестов к этим функциям?
  • Разумный. Является ли код, который я пишу, сильно неструктурированным и трудным для понимания?

Я могу сказать вам это, как только вы изучите функциональное программирование, эти проблемы будут легко решены.Изначально идея была в функциональном программировании.Как только вы освоите функциональное программирование, а затем изучите реактивное программирование, понять его будет легче.Я испытал это на себе. сам. Когда я раньше изучал Rxjs, это было очень болезненно.Честно говоря, Rxjs — самая сложная библиотека, которую я когда-либо изучал, без исключения. После мучительного месяца или двух некоторые вещи все еще не работают вместе, что я считаю само собой разумеющимся, пока недавно не изучил функциональное программирование. Без преувеличения, я постараюсь познакомить вас с Rxjs в следующих статьях, я также поделился этой темой с компанией.

Что такое функциональное программирование?

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

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

Кратко продемонстрируем всю прелесть функционального программирования на примере.

Текущим требованием является вывод вывода на веб-страницу.“Hello World”.

Может быть, новичок так и написал бы.

document.querySelector('#msg').innerHTML = '<h1>Hello World</h1>'

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

function printMessage(elementId, format, message) {
    document.querySelector(elementId).innerHTML = `<${format}>${message}</${format}>`
}

printMessage('msg', 'h1', 'Hello World')

Это немного улучшает, но это все еще не повторно используемый фрагмент кода, если я пишу текст в файл, а не не-HTML, или я хочу повторить отображениеHello World.

Итак, как бы вы написали этот код как функциональный разработчик?

const printMessage = compose(addToDom('msg'), h1, echo)

printMessage('Hello World')

Объясните этот код, гдеh1иechoфункции,addToDomТакже очевидно, что это функция, так почему же мы пишем ее именно так? Похоже, что функций гораздо больше.

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

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

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

Хорошо, давайте снова изменим требование, теперь нам нужно повторить текст три раза и вывести его в консоль.

var printMessaage = compose(console.log, repeat(3), echo)

printMessage(‘Hello World’)

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

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

  • декларативное программирование
  • чистая функция
  • Прозрачность котировок
  • неизменность

декларативное программирование

Функциональное программирование — это парадигма декларативного программирования: эта парадигмаописать ряд операций, но нетпоказать, как они реализованыиликак потоки данных передают их.

Известный SQL-оператор — очень типичный пример декларативного программирования, состоящий из одного за другимописыватьКакая композиция утверждений должна быть в результате запроса, и реализован внутренний механизм извлечения данныхАннотация.

Давайте посмотрим на другой набор кода и сравним императивное программирование с декларативным.

// 命令式方式
var array = [0, 1, 2, 3]
for(let i = 0; i < array.length; i++) {
    array[i] = Math.pow(array[i], 2)
}

array; // [0, 1, 4, 9]

// 声明式方式
[0, 1, 2, 3].map(num => Math.pow(num, 2))

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

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

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

чистая функция

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

Часто эти условия имеют побочные эффекты.

  • Изменить глобальную переменную, свойство или структуру данных
  • Изменить исходное значение параметра функции
  • Обработка пользовательского ввода
  • бросить исключение
  • трафаретная печать или журнал
  • Запрашивайте HTML-документы, файлы cookie браузера или получайте доступ к базам данных

Возьмем простой пример

var counter = 0
function increment() {
    return ++counter;
}

Эта функция нечиста. Она считывает внешние переменные. Вы можете подумать, что в этом коде нет проблем, но нам нужно знать, что такие расчеты, основанные на внешних переменных, трудно предсказать. Вы также можете быть в другом Значение счетчика где-то изменен, так что значение, которое вы увеличиваете, не соответствует ожидаемому.

Чистая функция обладает следующими свойствами:

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

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

Теперь у нас есть небольшое требование: найти запись студента по id и отрендерить ее в браузере (при написании программы подумайте, что она также может быть записана в консоль, базу данных или файл, поэтому подумайте, как сделать ваш код многоразовым) .

// 命令式代码

function showStudent(id) {
    // 这里假如是同步查询
    var student = db.get(id)
    if(student !== null) {
          // 读取外部的 elementId
          document.querySelector(`${elementId}`).innerHTML = `${student.id},${student.name},${student.lastname}`
    } else {
        throw new Error('not found')
    }
}

showStudent('666')

// 函数式代码

// 通过 find 函数找到学生
var find = curry(function(db, id) {
    var obj = db.get(id)
    if(obj === null) {
        throw new Error('not fount')
    }
    
    return obj
})

// 将学生对象 format
var csv = (student) => `${student.id},${student.name},${student.lastname}`

// 在屏幕上显示
var append = curry(function(elementId, info) {
    document.querySelector(elementId).innerHTML = info
})

var showStudent = compose(append('#student-info'), csv, find(db))

showStudent('666')

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

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

  • гибкий. Есть три многоразовых компонента
  • Декларативный стиль, обеспечивающий четкое представление высокоуровневых шагов и повышающий читабельность кода.
  • Другой — отделить чистые функции от нечистого поведения.

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

Прозрачность котировок

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

Эту концепцию легко понять, достаточно привести два примера.

// 非引用透明
var counter = 0

function increment() {
    return ++counter
}

// 引用透明
var increment = (counter) => counter + 1

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

неизменяемые данные

Неизменяемые данные — это данные, которые нельзя изменить после их создания. Как и во многих других языках, некоторые примитивные типы в JavaScript (String, Number и т. д.) по своей сути являются неизменяемыми, но объекты изменяются везде.

Рассмотрим простой код сортировки массива:

var sortDesc = function(arr) {
    return arr.sort(function(a, b) {
        return a - b
    })
}

var arr = [1, 3, 2]
sortDesc(arr) // [1, 2, 3]
arr // [1, 2, 3]

Этот код, кажется, не проблема, но он вызовет побочные эффекты в процессе сортировки, изменив исходную ссылку, вы можете видеть, что исходная запись становится[1, 2, 3]. Это недостаток языка, и как его преодолеть, будет описано далее.

Суммировать

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

Содержимое взято из «Руководства по функциональному программированию JavaScript».

Добро пожаловать, чтобы обратить внимание на личный публичный аккаунт [Front-end Taoyuan].Частота обновления публичного аккаунта выше, чем у Nuggets.