函数式编程
什么是函数式编程
函数式编程概念比较古老,现在大部分讲的是面向对象编程,但是函数式编程也有优点
- React的高阶组件使用了高阶函数,高阶函数就是函数式编程的一个概念。
- Vue3也拥抱了函数式编程
- 函数式编程可以抛弃this
- 工程化中可以更好的tree shaking过滤无用代码
- 方便测试
- 有很多库可以帮助我们开发 loadsh等
掌握函数式编程需要掌握的准备
函数式一等公民
- 函数可以存储在变量中
- 函数可以作为参数
- 函数可以作为返回值
高阶函数
高阶函数就是利用了函数可以作为参数和函数可以作为返回值的特性。常用的高阶函数有 reduce、forEach、map、filter、sort等
闭包
闭包:函数和它周围状态的引用捆绑到一起形成了闭包。如下
function makeFun() {
let name = '张三'
return function() {
return name
}
}
纯函数
对一个函数保持相同的输入能够有相同的输出的函数就是纯函数。如 slice
纯函数的好处
- 可以缓存
- 便于测试
柯里化
解决硬编码的问题
函数的柯里化指的是一个函数有多个参数时,先处理一部分参数,然后返回一个函数处理剩余部分参数
函数组合
什么是函数组合
如果一个函数经过多个函数处理才能得到最终值,这个时候可以吧中间部分的函数合成一个函数
解决了什么问题
解决了洋葱代码 h(g(f(x)))
函数的结果依赖于上一个函数的结果作为参数
如何使用
// 原函数
let fn = h(g(f(x)))
// 函数组合后, 默认从右向左执行,
let fn = compose(h, g, f)
实现方式
function compose (...args) {
// 接受一组处理函数, 返回一个函数接受初始值
return function (value) {
// 默认从右往左执行,使用reverse翻转事件数组, 使用reduce处理所有函数
return args.reverse().reduce(function (acc, fn) {
return fn(acc)
}, value)// acc的初始值为value
}
}
FP模块
loadsh中的FP模块相对于普通模块的区别
- 自动curry
- 函数优先、数据之后 (工具方法第一个参数优先函数,第二个数据)
函子
函子的构成
- 一个容器,里面包含一个值,和一个map方法,接受一个函数,来改变这个值
函子的作用
- 把函数式编程的副作用控制在一定范围之内
- 处理异常情况
- 处理异步操作
函子的构成
class Container {
static of(value) {
return new Container(value)
}
constructor(value) {
this._value = value
}
map(fn) {
return Container.of(fn(this._value))
}
}
let r = Container.of(3)
.map(x => x +1);
console.log(r);
MayBe函子
处理值为空的情况
class mayBe {
static of(value) {
return new mayBe(value)
}
constructor(value) {
this._value = value
}
map(fn) {
return this.isNothing() ? mayBe.of(null) : mayBe.of(fn(this._value))
}
isNothing() {
return this._value === null || this._value === undefined
}
}
let r = mayBe.of(3)
.map(x => x +1);
console.log(r);
Either函子
- Either两者中任何一个,类似于if…else…
- 出现异常Either函子能准确的抛出异常
- 异常会让函数不纯,Either函子会处理异常
class Either {
static of(value) {
return new Either(value)
}
constructor(value) {
this._value = value
}
map(fn) {
return this
}
}
class Container {
static of(value) {
return new Container(value)
}
constructor(value) {
this._value = value
}
map(fn) {
return Container.of(fn(this._value))
}
}
function parseJson(str) {
try {
return Container.of(JSON.parse(str))
} catch (e) {
return Either.of({error: e})
}
}
console.log(parseJson('{name:"xm"}'));
IO函子
该函子中的value是一个函数,它把动作储存到_value中,延迟处理。把该操作交给调用者处理。即把不纯的操作交给外部处理
const fp = require('lodash/fp')
class IO {
static of(value) {
// 接收一个值,把它包装为一个函数
return new IO(() => value)
}
constructor(fn) {
// 存储该函数
this._value = fn
}
map(fn) {
// 需要返回一个新的IO,是为了组合一个新的函数
// 组合两个函数
return new IO(fp.flowRight(fn, this._value))
}
}
const r = IO.of(process)
.map(p => p.execPath)
// 外部求值,有错误外部处理
console.log(r._value())