предисловие
Концепция внешнего функционального программирования существует уже давно. Я, возможно, использовал функциональный метод для написания кода в большей или меньшей степени в своих проектах, но я не изучал, что такое функциональное программирование. проверил некоторую информацию, прочитал несколько книг и обобщил свой опыт.
Определение функций высшего порядка
Если вы хотите понять функциональное программирование, вы должны сначала понять, что такое функции высшего порядка.
Определение функции высшего порядка:
Функции можно передавать в качестве параметров
Функции могут быть выведены как возвращаемые значения
Напишите код, используя функции высшего порядка, которые поставляются с 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, понятно?