предисловие
Концепция внешнего функционального программирования существует уже давно. Я, возможно, использовал функциональный метод для написания кода в большей или меньшей степени в своих проектах, но я не изучал, что такое функциональное программирование. проверил некоторую информацию, прочитал несколько книг и обобщил свой опыт.
Определение функций высшего порядка
Если вы хотите понять функциональное программирование, вы должны сначала понять, что такое функции высшего порядка.
Определение функции высшего порядка:
Функции можно передавать в качестве параметров
Функции могут быть выведены как возвращаемые значения
Напишите код, используя функции высшего порядка, которые поставляются с ES6.
Предположим, у нас есть такой массив:
const classA = [
{
name: '张三',
age: 17
},
{
name: '李四',
age: 15
},
{
name: '王五',
age: 16
},
]
Требуется найти 16-летних учеников в классе, мы используем функции младшего порядка для фильтрации следующим образом:
let student = [];
for (let i = 0; i < classA.length; i++) {
if (classA[i].age === 16) {
student.push(class[i])
}
}
Используйте функции более высокого порядка, например:
const student = classA.filter( v => v.age === 16 )
Итак, каковы преимущества использования таких функций более высокого порядка?Есть два момента:
- Во-первых, код той же сложности, функции более высокого порядка могут упростить реализацию.
- Во-вторых, функции высшего порядка могут легко разделить логику.
Например, такую функцию для фильтрации студентов можно разделить на две части:
const isAge = v => v.age === 16;
const result = classA.filter(isAge);
После этого разделения логика делится на две части, первая часть — это функция оценки возраста, а вторая — функция фильтрации результатов.
Если в будущем наши потребности изменились, и вместо скрининга возраста учащихся они изменились на скрининг имен студентов или еще что-то, то нам нужно изменить только функцию оценки возраста, а функцию скрининга результаты остаются неизменными.
Что ж, некоторые люди могут сказать, что это слишком просто, так что сделайте это немного сложнее!
Предположим, у нас есть такой массив:
const array = [['张三','26','1000'],['李四','25','3655'],['王五','30','8888']]
Мы собираемся превратить этот массив во что-то вроде этого:
[
{
name: '张三',
age: '26',
price: '1000'
},
{
name: '李四',
age: '25',
price: '3655'
},
{
name: '王五',
age: '30',
price: '8888'
},
]
Используйте функции высшего порядка для преобразования:
const result = array.reduce((value, item, index) => {
value[index] = {
name: item[0],
age: item[1],
price: item[2]
};
return value;
}, []);
Здесь мы используем функции высшего порядка ES6.reduce, для конкретного ознакомления вы можете посетить Bump Lab.Неполное руководство по методу Reduce() в JavaScript
Функции высшего порядка, поставляемые с ES6,filter,map,reduceИ т. д. и т. д.
Хорошо, на данный момент у меня есть несколько простых концепций функционального программирования.Я понимаю функциональное программирование:
Когда дело доходит до написания кода, функциональное программирование — это скорее декларативный подход, в то время как традиционное программирование — императивный подход. Например, в приведенном выше скрининге студенческого возраста традиционная идея программирования состоит в том, что я создал, что зациклил, что рассудил и какой результат получил; идея функционального программирования заключается в том, что я заявляю функция фильтрации, я объявляю функцию, которая оценивает, и я объединяю две функции, чтобы получить результат.
Напишите свою собственную функцию высшего порядка
Когда мы обыграли множество функций высокого порядка, поставляемых с ES6, мы можем перейти к этапу самостоятельного написания функций высокого порядка.Например, мы можем написать функцию регулирования функциональным способом.
Проще говоря, throttling function — это функция, которая управляет частотой срабатывания событий, раньше она могла срабатывать неограниченное количество раз в течение одной секунды, а теперь — срабатывает только раз в 500 миллисекунд.
throttle(fn, wait=500) {
if (typeof fn != "function") {
// 必须传入函数
throw new TypeError("Expected a function")
}
// 定时器
let timer,
// 是否是第一次调用
firstTime = true;
// 这里不能用箭头函数,是为了绑定上下文
return function (...args) {
// 第一次
if (firstTime) {
firstTime = false;
fn.apply(this,args);
}
if (timer) {
return;
}else {
timer = setTimeout(() => {
clearTimeout(timer);
timer = null;
fn.apply(this, args);
},wait)
}
}
}
// 单独使用,限制快速连续不停的点击,按钮只会有规律的每500ms点击有效
button.addEventListener('click', throttle(() => {
console.log('hhh')
}))
Написав такую функцию высшего порядка, мы можем вызывать ее везде, например:
// 有一个点击增加的功能,但是要求最少过了1秒才能增加一次,就可以
const add = x => x++;
throttle(add,1000);
// 又有了一个减少的功能,但是要求最少2秒减少一次
const cutDown = x => x--;
throttle(cutDown,2000);
До сих пор вы понимали, что такое функции высшего порядка, но этого недостаточно, вам также необходимо понимать некоторые важные концепции функционального программирования.
чистая функция
В концепции функционального программирования еще одним важным понятием является чистая функция, так что же такое чистая функция?
Давайте используем код, чтобы объяснить, что такое чистая функция:
const z = 10;
add(x, y) {
return x + y;
}
надaddФункция — это чистая функция, которая считываетxа такжеyзначение двух аргументов, возвращает их сумму и не зависит от глобальногоzвлияние переменных
изменить эту функцию
const z = 10;
add(x, y) {
return x + y + z;
}
Эта функция становится нечистой, потому что возвращаемое ею значение будет зависеть от глобальногоzВлияние
Другими словами, на эту функцию будет влиять внешняя среда.
Итак, мы получили первое важное основание для суждения о том, является ли чистая функция
1. Чистые функции не зависят от внешней среды.
повторное использованиеspliceа такжеsliceОбъяснить:
var xs = [1,2,3,4,5];
// 纯的
xs.slice(0,3);
//=> [1,2,3]
xs.slice(0,3);
//=> [1,2,3]
xs.slice(0,3);
//=> [1,2,3]
// 不纯的
xs.splice(0,3);
//=> [1,2,3]
xs.splice(0,3);
//=> [4,5]
xs.splice(0,3);
//=> []
sliceОна получает одни и те же параметры и каждый раз возвращает одно и то же значение, поэтому это чистая функция.
spliceОна получает одни и те же параметры и каждый раз возвращает разные значения, поэтому это не чистая функция.
Итак, мы приходим ко второй важной основе для судейства или нет
2. Чистые функции с одним и тем же входом всегда будут получать один и тот же результат.
Подводя итог, чистые функции:
'纯函数是这样一种函数,即相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用'
Так что же副作用, в чистой функции есть такое определение:
Все, что происходит помимо результата самой функции, называется побочным эффектом.
Как и в приведенном выше примере, результат, возвращаемый функцией, зависит от внешнихzЕсли переменная влияет на функцию, то функция имеет побочные эффекты, наоборот, функция влияет на внешнюю среду и тоже имеет побочные эффекты.
К этому моменту я наконец-то понял, что такое чистая функция, и у нее есть следующие преимущества:
- легче тестировать, потому что их единственная работа — вычислять выходные данные на основе входных данных.
- Результаты могут быть кэшированы, потому что один и тот же ввод всегда будет получать один и тот же вывод.
- Самодокументируемость, так как зависимости функции понятны.
- Легче вызывать, потому что вам не нужно беспокоиться о побочных эффектах функции.
Использование чистых функций может значительно снизить сложность программирования, но неразумное использование, снятие абстракции ради абстракции, сделает код очень трудным для понимания.
карри
Концепция каррирования проста: вызовите функцию только с подмножеством аргументов и заставьте ее вернуть функцию для обработки остальных аргументов.
const add = x => y => x + y;
add(1)(2);
// => 3
Приведенный выше пример представляет собой типичную каррированную функцию, когда мы вызываем ее в первый раз, она получает первый переданный параметр (запоминается с замыканием) и возвращает новую функцию, во втором случае при втором вызове она получает параметры переданные во второй раз, добавляют их к функции, переданной в первый раз, и возвращают их сумму.
Этот пример иллюстрирует особенность каррирования или основу, то есть функции каррирования имеют особенность ленивых вычислений, и для достижения этой особенности требуются некоторые средства.
Напишите каррированную функцию, используя приведенные выше идеи.
// 创建柯里化函数,保存了第一次传入的参数和函数,返回值是一个函数并且接收第二次传入参数,同时调用传入的函数进行计算
currying (fn, ...args1) {
return (...args2) => {
return fn(...args1, ...args2)
}
}
// 定义一个一般函数
const add = (x, y) => x + y;
// 使用
const increment = currying(add, 1);
console.log(increment(2));
const addTen = currying(add, 10);
console.log(addTen(2));
// => 3
// => 12
В этом примере все еще есть небольшая проблема, то есть возвращаемое значение не каррируется автоматически и может быть изменено:
currying(fn, ...args1) {
// '判断传入的参数是否满足传入函数需要的参数,比如说add函数需要两个参数相加,那么判断是否传入了两个参数,满足调用传入函数计算结果'
if (args1.length >= fn.length) {
console.log(args1, '--1--');
return fn(...args1);
}
// '不满足返回一个新的函数,继续调用柯里化函数,传入保存的第一次传入的函数,传入保存的第一次传入的参数,传入第二次传入的参数,继续上面的判断逻辑,返回计算结果'
return (...args2) => {
console.log(args2, '--2--');
return currying(fn, ...args1, ...args2);
};
},
// 定义一个一般函数
const add = (x, y) => x + y;
// 使用
const increment = currying(add, 1);
console.log(increment(2));
const addTen = currying(add, 10);
console.log(addTen(2));
// => [2] --2--
// => [1,2] --1--
// => 3
// => [2] --2--
// => [10,2] --1--
// => 12
Функция является первоклассным гражданином в js. Она ничем не отличается от других объектов или других данных. Она может существовать в массиве, объекте, присваивать ее переменной и передавать ее как параметр, поэтому функция также имеет атрибут нижнего индекса, используя приведенный выше пример, чтобы доказать
const add = (x, y) => x + y;
console.log(add.length)
// => 2
В ES6,...является оператором спреда, его использование похоже на это
// 放在函数作为单独参数,会把一个数组变成参数序列,比如上面例子中的数组[1,2]变成了参数x=1,y=2
fn(...args1)
// 放在函数中作为第二个参数,会把传入的值变成一个数组,如果传入的是一个数组那么还是数组,传入一个对象,会变成一个数组对象
function currying(fn,...x) {
console.log(x)
}
currying(0,1)
// => [1]
// 放在回调函数中作为第二个和第三个参数
// 第一次调用会返回一个函数,会在闭包里存贮值,第二次调用会把闭包里的值和第二次参数里的值合并成数组
return currying(fn, ...args1, ...args2);
// => [1,2]
// 但是单独在函数中这么使用会报错
function currying(fn,...x,...y) {
console.log(x)
}
currying(0,1,2)
Поймите это, приведенный выше пример легко понять.
Наиболее важными идеями каррирования функций являются:
Определить, соответствуют ли входящие параметры требованиям расчета много раз, и вернуть результат расчета, если он удовлетворен.Если нет, продолжить возвращать новую каррирующую функцию
Вышеуказанные каррированные функции еще можно оптимизировать, например вот эту привязку, специальные заполнители переменных и т.д. Для такой работы некоторые библиотеки, напримерramdaОна реализована, вы можете увидеть, как она реализована в исходном коде, главное понять, что такое каррирующая функция.
Комбинации кодов
Во-первых, напишите простую функцию композиции:
const compose = (f, g) => x => f(g(x));
Эта составная функция принимает две функции в качестве аргументов и возвращает новую функцию, где x — это значение, которое будет использоваться между двумя функциями, например:
// 我们要实现一个给字符串全部变成大写,然后加上个感叹号的功能,只需要定义两个函数,然后组合一下
const toUpperCase = x => x.toUpperCase();
const exclaim = x => `${x}!`;
const shout = compose(exclaim, toUpperCase);
shout('hello world')
// => HELLO WORLD!
Примечание. В функции композицииgкоэффициент функцииfФункция выполняется первой, поэтому в композиции она выполняется справа налево, то есть функция, выполняемая первой, должна располагаться справа от функции композиции.
Эта комбинированная функция все еще немного проблематична. Она может принимать только 2 параметра. Давайте сделаем небольшую модификацию, чтобы сделать ее сильнее:
const compose = (...fns) => (...args) => fns.reduceRight((res, fn) => [fn.call(null, ...res)], args)[0];
// 使用,实现一个功能,字符串变成大写,加上个感叹号,还要截取一部分,再在前面加上注释
const toUpperCase = x => x.toUpperCase();
const exclaim = x => `${x}!`;
const head = x => `slice is: ${x}`;
const reverse = x => x.slice(0, 7);
const shout = compose(exclaim, toUpperCase, head, reverse)
shout('my name is maya')
// => SLICE IS: MY NAME!
Принцип комбинации на самом деле является ассоциативным законом в математике:
(a + b) + c = a + (b + c)
Итак, в сочетании вы можете сделать это
// 第一种
const one = compose(exclaim, toUpperCase)
const shout = compose(one, head, reverse)
shout('my name is maya')
// => SLICE IS: MY NAME!
// 第二种
const two = compose(toUpperCase, head)
const shout = compose(exclaim, two, reverse)
shout('my name is maya')
// => SLICE IS: MY NAME!
// 第三种
const three = compose(head, reverse)
const shout = compose(exclaim, toUpperCase, three)
shout('my name is maya')
// => SLICE IS: MY NAME!
...
Итак, на данный момент мое понимание комбинации таково:
Что такое комбинация Комбинация — это использование ассоциативного закона в математике, как строительных блоков, для соединения различных функций и обеспечения потока данных в нем.
Есть комбинированные функции в разных библиотеках,lodash,underscore,ramdaждать, например, вunderscoreВнутри состав выглядит так:
// Returns a function that is the composition of a list of functions, each
// consuming the return value of the function that follows.
_.compose = function() {
var args = arguments;
var start = args.length - 1;
return function() {
var i = start;
var result = args[start].apply(this, arguments);
while (i--) result = args[i].call(this, result);
return result;
};
};
В сочетании с
Ну, на данный момент у меня есть предварительное понимание концепции функционального программирования, так как же мы можем использовать функциональное программирование для написания кода, например:
// 伪代码,思路
// 比如说,我们请求后台拿到了一个数据,然后我们需要筛选几次这个数据, 取出里面的一部分,并且排序
// 数据
const res = {
status: 200,
data: [
{
id: xxx,
name: xxx,
time: xxx,
content: xxx,
created: xxx
},
...
]
}
// 封装的请求函数
const http = xxx;
// '传统写法是这样的'
http.post
.then(res => 拿到数据)
.then(res => 做出筛选)
.then(res => 做出筛选)
.then(res => 取出一部分)
.then(res => 排序)
// '函数式编程是这样的'
// 声明一个筛选函数
const a = curry()
// 声明一个取出函数
const b = curry()
// 声明一个排序函数
const c = curry()
// 组合起来
const shout = compose(c, b, a)
// 使用
shout(http.post)
Как официально использовать функциональное программирование в своих проектах
Я думаю, что есть несколько шагов для формального использования функционального программирования в проекте:
- 1. Сначала попробуйте использовать функции более высокого порядка, которые поставляются с ES6.
- 2. После того, как вы ознакомитесь с функциями высшего порядка, которые поставляются с ES6, вы можете попробовать написать несколько функций более высокого порядка самостоятельно.
- 3. В этом процессе попробуйте использовать чистые функции для записи кода
- 4. Как только у вас появится некоторое представление о функциональном программировании, попробуйте использовать что-то вроде
ramdaбиблиотека для написания кода - 5. В использовании
ramdaВ процессе можно попробовать изучить его исходный код - 6. Попробуйте написать собственную библиотеку, каррировать функции, комбинировать функции и т. д.
Конечно, это только мое собственное понимание, и я не полностью использовал разработку функционального программирования в реальных проектах.Мои принципы разработки:
不要为了函数式而选择函数式编程。 If the function programming can help you, you can upgrade the efficiency, quality, you can use; if you can't, then don't use it; if you are still not familiar with the function, such as what I like, occasionally использовать
расширять
Функциональное программирование разрабатывается на основе теории категорий, а о взаимосвязи между функциональным программированием и теорией категорий Руан Ифэн дал хорошее объяснение, скопируйте и вставьте его статью сюда
По сути, функциональное программирование — это просто метод работы теории категорий, это то же самое, что математическая логика, исчисление, определитель — все это математические методы, но бывает, что их можно использовать для написания программ.
Итак, вы понимаете, почему функциональное программирование требует, чтобы функции были чистыми и не имели побочных эффектов? Поскольку это математическая операция, исходная цель состоит в том, чтобы оценить и ничего больше не делать, иначе алгоритм функции не может быть удовлетворен.
наконец
Мой уровень ограничен, и есть ошибки и упущения.Надеюсь, боссы могут указать на многое и распылить слегка одновременно! ! !
Ссылаться на
Инкапсуляция функций дросселирования и устранения дребезга с помощью функционального программирования
Изучите функциональное программирование на JavaScript (часть 1)
Mostly adequate guide to FP
Разумное использование чистого функционального программирования
Введение в функциональное программирование
Здоровяк, каррирование JavaScript, понятно?