Автор оригинала: FreeCodeCamp Community Member.
JavaScript – это мультипарадигма языка и может быть написана после разных парадигм программирования. Парадигма программирования по сути является кучей правил, которые вы следуете при записи кода.
Эти парадигмы существуют, потому что они решают проблемы, которые представляют собой программистов, и у них есть свои собственные правила и инструкции, чтобы помочь вам написать лучший код.
Каждая парадигма поможет вам решить конкретную проблему. Так что это полезно иметь обзор каждого из них. Мы будем покрывать функциональное программирование здесь.
В конце этой статьи есть некоторые ресурсы, которые вы можете использовать, чтобы пойти дальше, если вам понравилось это введение.
Существует также глоссарий GitHub, который поможет вам декодировать часть жаргона, который использует функциональное программирование.
Наконец, вы найдете место, чтобы получить руки грязные кодировки с практическими примерами и репо GitHub, полным ресурсов, которые вы можете использовать, чтобы узнать больше. Так что давайте погрузимся в.
Декларативные VS императивные парадигмы
Одним из примеров этих парадигм я говорил в начале является объектно-ориентированным программированием. Другое – это функциональное программирование.
Так что же именно функциональное программирование?
Функциональное программирование – это подпарадигма Декларативное программирование Парадигма, со своими собственными правилами, чтобы следовать при написании кода.
Что такое декларативная парадигма программирования?
Если вы кодируете на языке, который следует за декларативной парадигмой, вы пишете код, который указывает То, что вы хотите сделать, не говоря, как.
Супер простой пример этого является либо SQL, либо HTML:
SELECT * FROM customers
В приведенных выше примерах кода вы не реализуете Выберите
или как сделать визуализацию Div
Отказ Вы просто говорите компьютер что делать, без Как Отказ
Из этой парадигмы, такие как Функциональное программирование. Больше на этом ниже.
Какая императивная парадигма программирования?
Если вы кодируете на языке, который следует за императивной/процедурной парадигмой, вы пишете код, который рассказывает Как что-то сделать.
Например, если вы делаете что-то вроде ниже:
for (let i = 0; i < arr.length; i++) { increment += arr[i]; }
Вы говорите компьютеру точно, что делать. Итайте через массив под названием arr
, а потом увеличение
каждый из предметов в массиве.
Декларативный против императивного программирования
Вы можете написать JavaScript в Декларативная парадигма или Императивная парадигма. Это то, что люди означают, когда говорят, что это мультипарадигма. Это просто тот функциональный код следует за Декларативная парадигма Отказ
Если это поможет вам помнить, пример декларативной команды будет задать компьютер сделать вам чашку чая (мне все равно, как вы это делаете, просто принеси мне чай).
В то время как нет, вы должны сказать:
- Иди на кухню.
- Если в комнате есть чайник, и у него достаточно воды для чашки чая, включите чайник.
- Если в комнате есть чайник, и у него недостаточно воды для чашки чая, наполните чайник достаточно воды для чашки чая, затем включите чайник.
- И так далее
Так что такое функциональное программирование?
Так что это означает для функционального кода?
Потому что это подпададигма из Декларативная парадигма Это влияет на то, как вы пишете функциональный код. Обычно он приводит к меньшему коду, потому что JavaScript уже имеет много встроенных функций, которые вам обычно нуждаются. Это одна из тех, по которой люди любят функциональный код.
Это также позволяет вам сильно абстрагироваться (вам не нужно понимать, как что-то сделано), вы просто называете функцию, которая делает это для вас.
И какие правила, которые приводят к функциональному коду?
Функциональное программирование можно просто объяснить, следуя этим 2 законам в вашем коде:
- Вы архитектор вашего программного обеспечения из чистых, изолированных функций
- Вы избегаете подключения и побочных эффектов
Давайте копать в этом.
1. Архитектор вашего программного обеспечения из чистых, изолированных функций
Давайте начнем в начале,
Функциональный код делает тяжелое использование нескольких вещей:
Чистые функции
Тот же вход всегда дает тот же выход ( Idempotence ) и не имеет побочных эффектов.
Idempotent Функция Это тот, который, когда вы снова примените результаты к этой функции, не дают другого результата.
/// Example of some Math.abs uses Math.abs('-1'); // 1 Math.abs(-1); // 1 Math.abs(null); // 0 Math.abs(Math.abs(Math.abs('-1'))); // Still returns 1 Math.abs(Math.abs(Math.abs(Math.abs('-1')))); // Still returns 1
Побочные эффекты – это когда ваш код взаимодействует с (читает или пишет на) внешнее мнение.
Внешнее воспоминание – это буквально что-либо вне функции, которая изменила бы данные в вашей программе. Установите функцию? Установите логию на объекте? Удалить свойства на объекте? Все изменения в штате за пределами вашей функции.
function setAvailability(){ available = true; }
Изолированные функции
Нет зависимости от состояния программы, которая включает в себя глобальные переменные, которые могут быть изменены.
Мы обсудим это дальше, но все, что вам нужно, следует передавать в функцию в качестве аргумента. Это делает ваши зависимости (вещи, которые функция должна выполнять свою работу) гораздо яснее, чтобы увидеть, и более обнаружена.
Хорошо, так почему ты делаешь такие вещи?
Я знаю, что это похоже на много ограничений, которые делают ваш код излишне сложным. Но они не ограничиваются ограничениями, они являются руководящими принципами, которые пытаются остановить вас от падения в шаблонах, которые обычно приводят к ошибкам.
Когда вы не изменяете свой код кода, высказав свой код Если
на основании Логический
Состояние, установленное несколькими местами в вашем коде, вы делаете код более предсказуемым и легче рассуждать о том, что происходит.
Когда вы следуете функциональной парадигме, вы обнаружите, что порядок выполнения вашего кода не имеет значения.
Это имеет довольно много преимуществ – одно существо, например, что для копирования ошибки вам не нужно точно знать, что каждый Логический
и Объект
состояние было до того, как вы запустите свои функции. Пока у вас есть стек вызова (вы знаете, какая функция работает/работает до вас), она может воспроизвести ошибки и решить их легче.
Воспроизводимость через функции высшего порядка
Функции, которые могут быть назначены переменной, передаваемой в другую функцию или возвращаются из другой функции, как и любое другое нормальное значение, называются Функции первого класса Отказ
В JavaScript все функции являются функциями первого класса. Функции, которые имеют статус первого класса, позволяют нам создавать Функции высшего порядка Отказ
А Функция высшего порядка Это функция, которая либо принимает функцию в качестве аргумента, возвращает функцию, либо оба! Вы можете использовать функции более высокого порядка, чтобы перестать повторять себя в вашем коде.
Что-то вроде этого:
// Here's a non-functional example const ages = [12,32,32,53] for (var i=0; i < ages.length; i++) { finalAge += ages[i]; } // Here's a functional example const ages = [12,32,32,53] const totalAge = ages.reduce( function(firstAge, secondAge){ return firstAge + secondAge; })
Встроенный JavaScript Массив
Функции .map
, .Расмейте
и .filter
Все принимают функцию. Они отличные примеры Функции более высокого порядка, Поскольку они повторяют на массиве и позвоните в функцию, которую они получили для каждого элемента в массиве.
Так что вы могли бы сделать:
// Here's an example of each const array = [1, 2, 3]; const mappedArray = array.map(function(element){ return element + 1; }); // mappedArray is [2, 3, 4] const reduced = array.reduce(function(firstElement, secondElement){ return firstElement + secondElement; }); // reduced is 6 const filteredArray = array.filter(function(element){ return element !== 1; }); // filteredArray is [2, 3]
Передача результатов функций в другие функции или даже прохождение самих функций, в функциональном коде чрезвычайно распространено в функциональном коде. Я включил это краткое объяснение из-за того, как часто он используется.
Эти функции также часто используются, потому что они не изменяют базовую функцию (без изменений состояния), но работают на копии массив
Отказ
2. Избегайте мультипликации и побочных эффектов
Второе правило состоит в том, чтобы избежать мультипликации – мы затронули это кратко, когда мы говорили о ограничивании изменений на внешние смежные состояния – и побочные эффекты.
Но здесь мы расширим дальше. По сути, это сводится к этому: не меняйте вещи! Как только вы сделали это, это неизменный (без изменений со временем).
var ages = [12,32,32,53] ages[1] = 12; // no! ages = []; // no! ages.push("2") // no!
Если что-то может измениться для ваших структур данных, внесите изменения в копию.
const ages = [12,32,32,53] const newAges = ages.map(function (age){ if (age == 12) { return 20; } else { return age; } })
Можете ли вы увидеть, что я сделал копию с моими необходимыми изменениями?
Этот элемент повторяется снова и снова. Не меняйте состояние!
Если мы будем следовать этому правилу, мы будем тяжело использовать Const
Итак, мы знаем, что все не изменится. Но это должно идти дальше, чем это. Как насчет ниже?
const changingObject = { willChange: 10 } changingObject.willChange = 10; // no! delete obj.willChange // no!
Свойства ChangingObject
следует полностью заблокировать. Const
будет защищать вас только от инициализации по переменной.
const obj = Object.freeze({ cantChange: 'Locked' }) // The `freeze` function enforces immutability. obj.cantChange = 0 // Doesn't change the obj! delete obj.cantChange // Doesn't change the obj! obj.addProp = "Gotcha!" // Doesn't change the obj!
Если мы не можем изменить состояние глобальных переменных, то нам нужно обеспечить:
- Мы объявляем функциональные аргументы – любые вычисления внутри функции зависит только от аргументов, а не на каком-либо глобальном объекте или переменной.
- Мы не изменяем переменную или объекту – создавайте новые переменные и объекты и возвращайте их, если это необходимо из функции.
Сделайте свой код переданным прозрачным
Когда вы следуете правилу никогда не меняете состояния, ваш код становится переданный прозрачный Отказ То есть ваши вызовы функций могут быть заменены значениями, которые они представляют, не влияя на результат.
Как простой пример проверки, если ваш код Председается прозрачный, Смотри на Ниже приведен кодовый фрагмент:
const greetAuthor = function(){ return 'Hi Kealan' }
Вы должны быть в состоянии просто поменять этот звонок с помощью строка
Он возвращается и не имеет проблем.
Функциональное программирование со ссылками прозрачные выражения заставляют вас начать думать о вашем коде, если вы привыкли к Объектная ориентация Отказ
Но почему?
Потому что вместо объектов и воспоминания в вашем коде вы начинаете иметь чистые функции, без изменения состояния. Вы очень четко понимаете, что вы ожидаете, что ваша функция возвращается (так как она никогда не меняется, когда обычно она может вернуть разные типы данных в зависимости от состояния вне функции).
Он может помочь вам лучше понять поток лучше, понять, какая функция делает, просто снимая ее, и быть более строгим с помощью каких-либо обязанностей каждая функция, чтобы придумать лучшие развязанные системы.
Вы можете узнать больше о референтной прозрачности здесь Отказ
Нетерпение
Надеюсь, если вы набрали внимание, вы видите, что мы не меняемся. Так что просто чтобы быть понятным для
Петли выходят из окна:
for(let i = 0; i < arr.length; i++) { total += arr[i]; }
Потому что мы изменяем там государство переменной. Используйте карта
Функция более высокого порядка.
Больше функций функционального программирования
На этот вопрос я надеюсь, что у вас хороший обзор того, какой функциональный код есть и нет. Но есть некоторые окончательные концепции, используемые в функциональном коде, которые мы должны покрыть.
Во всех функциональном коде я прочитал, эти концепции и инструменты используются больше всего, и мы должны охватывать их, чтобы получить наши основные знания.
Так что здесь мы идем.
Рекурсия в функциональном программировании
Это возможно в JavaScript, чтобы вызвать функцию из самой функции.
Так что мы всегда могли бы сделать:
function recurse(){ recurse(); }
Проблема с этим – это то, что она не полезна. Это будет работать в конце концов, пока не сбивает ваш браузер. Но идея рекурсии – это функция, вызывающая себя от своего функционального тела. Итак, давайте посмотрим более полезный пример:
function recurse(start, end){ if (start == end) { console.log(end) return; } else { console.log(start) return recurse(start+1, end) } } recurse(1, 10); // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
Этот фрагмент кода будет рассчитывать от Начать
Аргумент для конец
аргумент И это делает это, позвонив свою собственную функцию снова.
Таким образом, порядок этого будет выглядеть что-то подобное:
Добавьте отладчик в блоках IF, чтобы следовать за этим, если это не имеет смысла для вас. Рекурсия – это один инструмент, который вы можете использовать для итерации в функциональном программировании.
Что делает первый пример и второй пример другой? Второй имеет то, что мы называем «Базовый случай» Отказ Базовый случай позволяет функции в конечном итоге перестать вызывать в себя бесконечно. Когда Начать
равно конец
Мы можем перестать речь. Как мы знаем, мы рассчитывали до самого конца нашего цикла.
Но каждый вызов функций снова вызывает свою собственную функцию и добавляет к аргументу функции.
Пример кода, который я только что включен в пример подсчета, не является чистая функция Отказ Это почему?
Потому что Консоль
это состояние! И мы регистрировали строка
к этому.
Это было краткое введение в рекурсию, но не стесняйтесь идти сюда, чтобы узнать больше здесь Отказ
Зачем использовать рекурсию?
Рекурсия позволяет нам прекратить мутационные переменные состояния для одного.
Есть также определенные структуры данных (структуры деревьев), которые более эффективны при решении с рекурсией. Они обычно требуют меньшего кода, поэтому некоторые кодеры любят читабельность рекурсии.
Карри в функциональном программировании
Carrying является еще одним инструментом, используемым в функциональном коде. Ариность функции относится к тому, сколько аргументов он получает.
// Let's talk arity function arity2(arg1, arg2){} // Function has an arity of 2 function arity0(){} // Function has an arity of 0 function arity2(arg1, arg2, arg3, arg4){} // Function has an arity of 4
Carrying Функция превращает функцию, которая имеет активность более 1, до 1. Это делает это, возвращая внутреннюю функцию, чтобы сделать следующий аргумент. Вот пример:
function add(firstNum, secondNum){ return firstNum + secondNum; } // Lets curry this function function curryAdd(firstNum){ return function(secondNum){ return firstNum + secondNum; } }
По сути, это реструктурирует функцию, поэтому требуется один аргумент, но затем возвращает другую функцию, чтобы сделать следующий аргумент, столько раз, сколько нужно.
Зачем использовать карри?
Большое преимущество Carrying является когда вам необходимо повторно использовать ту же функцию несколько раз, но меняйте только один (или меньше) параметров. Таким образом, вы можете сохранить первый вызов функции, что-то вроде этого:
function curryAdd(firstNum){ return function(secondNum){ return firstNum + secondNum; } } let add10 = curryAdd(10); add10(2); // Returns 12 let add20 = curryAdd(20); add20(2); // Returns 22
Carrying также может облегчить вашу код для рефакторов. Вам не нужно менять несколько мест, где вы проходите в неправильных функциях аргументах – просто одно место, где вы связаны с первым вызовом функции неправильному аргументу.
Также полезно, если вы не можете предоставить все аргументы функции за один раз. Вы можете просто вернуть первую функцию, чтобы вызвать внутреннюю функцию, когда у вас есть все аргументы позже.
Частичное приложение в функциональном программировании
Аналогичным образом, частичное применение означает, что вы примените несколько аргументов в функцию одновременно и вернуть другую функцию, которая применяется к большему количеству аргументов. Вот лучший пример, который я нашел из документов MDN:
const module = { height: 42, getComputedHeight: function(height) { return this.height + height; } }; const unboundGetComputedHeight = module.getComputedHeight; console.log(unboundGetComputedHeight(32)); // The function gets invoked at the global scope // outputs: NaN // Outputs NaN as this.height is undefined (on scope of window) so does // undefined + 32 which returns NaN const boundGetComputedHeight = unboundGetComputedHeight.bind(module); console.log(boundGetComputedHeight(32)); // expected output: 74
привязывать
является лучшим примером частичного приложения. Почему?
Потому что мы возвращаем внутреннюю функцию, которая назначается на BudgetComputedheight
Что вызывается, с это
Область применения правильно настроена и новый аргумент, переданный позже. Мы не назначали все аргументы одновременно, но вместо этого мы вернули функцию, чтобы принять остальные аргументы.
Зачем использовать частичное приложение?
Вы можете использовать частичное приложение всякий раз, когда вы не можете пройти все свои аргументы одновременно, но можете вернуть Функция
с функциями более высокого порядка, чтобы иметь дело с остальными аргументами.
Функциональная композиция в функциональном программировании
Последняя тема, которую я думаю, является фундаментальным для функционального кода, это Функциональная композиция Отказ
Функциональная композиция Позволяет нам принимать две или более функции и превратить их в одну функцию, которая имеет именно то, что делают две функции (или более).
// If we have these two functions function add10(num) { return num + 10; } function add100(num) { return num + 100; } // We can compose these two down to => function composed(num){ return add10(add100(num)); } composed(1) // Returns 111
Вы можете взять это дополнительно и создавать функции для сочинения любого количества нескольких функций ARITE вместе, если вам нужно, что для вашего использования в использовании.
Зачем использовать функциональную композицию?
Композиция позволяет вам структурировать ваш код из повторных использований функций, чтобы перестать повторять себя. Вы можете начать лечить функции, такие как небольшие строительные блоки, вы можете объединить вместе, чтобы достичь более сложного выхода.
Затем они становятся «единицами» или мощностью вычислений в ваших программах. Они много маленьких функций, которые работают в целом, все в составе более крупных функций, чтобы сделать «реальную» работу.
Это мощный способ архитектуры вашего кода и удерживает вас от создания огромных функций, скопированных и вставленных с крошечными различиями между ними.
Это также может помочь вам проверить, когда ваш код не будет плотно связан. И это делает ваш код более многоразовым. Вы можете просто изменить композицию ваших функций или добавить более крошечные функции в композицию, а не наличие всех копирований кода и вставленных по всему кодовой базе (для того, когда вам нужно, чтобы это сделать что-то подобное, но не совсем такое же, как и другая функция) Отказ
Пример ниже сделан тривиальным, чтобы помочь вам понять, но я надеюсь, что вы видите силу Функциональная композиция.
/// So here's an example where we have to copy and paste it function add50(num) { return num + 50; } // Ok. Now we need to add 30. But we still ALSO need elsewhere to add 50 still // So we need a new function function add30(num){ return num + 30; } // Ugh, business change again function add20(num){ return num + 20; } // Everytime we need to change the function ever so slightly. We need a new function //Let's use composition // Our small, reusable pure function function add10(num){ return num + 10; } function add50Composed(num){ return add10(add10(add10(add10(addNum(num))))); } function add30Composed(num){ return add10(add10(add10(num))); } function add20Composed(num){ return add10(add10(num)); }
Видите ли вы, как мы составили новые функции из меньших, чистых функций?
Заключение
Эта статья много охватывала. Но я надеюсь, что он пояснил функциональный код просто вместе с некоторыми из повторяющихся шаблонов, которые вы увидите снова и снова, в функциональном и даже нефункциональном коде.
Функциональный код не обязательно является лучшим, и ни одно объектно-ориентированный код. Функциональный код обычно используется для более математических проблем, таких как анализ данных. Это также очень полезно для систем в реальном времени высокой доступности, таких как вещи, написанные в Erlang (функциональный язык). Но это искренне зависит от проблемы с проблемой.
Я публикую свои статьи на Twitter Отказ Если вам понравилось эту статью, вы можете прочитать больше там.
Как узнать больше
Начните здесь, с введением FreeCodeCamp в функциональное программирование с помощью JavaScript.
Смотри здесь Для некоторых библиотек вы можете включить и воспроизводить и играть, чтобы действительно овладеть функциональным программированием.
Pureuse это Хороший обзор много функциональных концепций.
Наконец, Вот Отличный жаргонский глоссарий функциональных терминов.