【学习笔记】函数式编程
一、相关概念
函数式编程(Functional Programming,FP):
- 把现实世界的事物和事物之间的联系抽象到程序世界(对运算过程进行抽象)。
- 是一种编程范式(编程风格),同纬度有面向对象编程、面向过程编程。
函数式编程的思维方式:
- 函数式编程中的函数指的不是程序中的函数(方法),而是数学中的函数即映射关系(x->f(映射、联系)->y,y=f(x))。
- 相同的输入始终要得到相同的输出(纯函数)
函数是一等公民(First-class Function)
- 函数可以存储在变量中(函数表达式)
- 函数作为参数
- 函数作为返回值
高阶函数(Higher-order function)
- 高阶函数:函数作为参数 或 函数作为返回值
- 常用的高阶函数:forEach、map、filter、every、some、find/findIndex、reduce、sort
纯函数:相同的输入永远的到相同的输出,而且没有任何可观察的副作用
- 好处:
- 可缓存
- 可测试
- 并行处理
- 副作用:让一个函数变得不纯——依赖于外部的状态,无法保证相同输出
- 配置文件
- 数据库
- 全局变量
- 获取用户的输入
柯里化(Currying):解决硬编码问题
- 柯里化可以让我们给一个函数传递较少的参数得到一个已经记住了某些固定参数的新函数
- 柯里化内部使用了闭包来对函数参数进行缓存
- 让函数更灵活,颗粒度更小
- 可以把多元函数转换成一元函数,可以组合使用函数产生强大功能
函数组合(compose):如果一个函数要经过多个函数处理才能得到最终值,这个时候可以把中间过程合并成一个函数
- 类似数据管道
- 函数组合默认从右到左执行
- 函数组合要满足结合律(associativity)
- 假如compose(f,g,h),既可以把f和g组合,也可以把g和h组合,结果都是一样的
函数组合的作用:可以避免洋葱代码,把细粒度的函数重新组合成一个新的函数。
函数式编程相关库:lodash、underscore、ramda
lodash中的组合函数:
- flow 从左到右运行
- flowRight 从右到左运行
Lodash/fp
- lodash 的fp 模块提供了实用的对函数式编程友好的方法
- 提供了不可变 *auto-curried iteratee-first data-last** 的方法
Pointfree: 函数风格。
- 不需要指明处理的数据
- 只需要合成运算过程
- 需要定义一些辅助的基本运算函数
Functor (函子):是一个特殊的容器,通过一个普通的对象来实现,该对象具有map方法,map方法可以运行一个函数对值进行处理(变形关系)
- 容器:包含值和值的变形关系(这个变形关系就是函数)
- 函子:
- MayBe 函子:可以对外部的空值情况做处理(控制副作用在允许的范围)
- Either 函子:二选一,两者中的任意一个。类似于if...else;异常会让函数遍的不纯,Either 函子可以用来做异常处理
- IO 函子:IO 函子中的_value 是一个函数,这里是把函数作为值来处理;可以把不纯的动作存储到_value 中,延迟执行这个不纯的操作(惰性执行),包装当前的操作纯;把不纯的操作交给调用者来处理
- Task 异步执行:使用folktale中的compose、curry
- Pointed 函子:Pointed函子是实现了of静态方法的函子;of方法为了避免new来创建对象,更深层的含义是of方法用来把值放到上下文Context
- Monad 函子:Monad 函子是可以变扁的Pointed 函子,IO(O(x));一个函子如果具有 join 和 of 两个方法并遵守一些定律就是一个Monad
二、相关代码
// 闭包——函数作为返回值
function makeFn() {
let msg = 'Hello function'
return function() {
console.log(msg)
}
}
const fn = makeFn()
fn()
// 记忆函数
function memoize(f) {
let cache = {}
return function() {
let key = JSON.stringify(arguments)
cache[key] = cache[key] || f.apply(f, arguments)
return cache[key]
}
}
function getArea(r) {
console.log(r)
return Math.PI * r * r
}
let getAreaWithMemory = memoize(getArea)
console.log(getAreaWithMemory(4))
console.log(getAreaWithMemory(4))
console.log(getAreaWithMemory(4))