1. 什么是函数式编程
- 与面向对象编程,过程式编程 并列的编程范式(编程式思想)
- 核心思想:把运算过程抽象成函数,在编程的过程中面向函数进行编程
- 最主要特征:函数是第一等公民
- 只有纯的,没有副作用的函数
2. 函数是一等公民
- 函数可以存储在变量中
- 函数可作为返回值
- 函数可作为参数
let arr = [1, 2, 3, 4]
const map = (array, fn) => {
let results = []
for (let value of array) {
results.push(fn(value))
}
return results
}
arr = map(arr, v => v * v)
console.log(arr)
3. 高级函数-纯函数
a.概念:
- 纯函数对于相同的输入永远得到相同的输出
- 没有任何的副作用
- 纯函数中的函数指的是数学中的函数
b.好处:
-
在多线程环境下并行操作共享的内存数据很有可能会出现意外情况
-
纯函数不需要访问共享的数据,所以在并行环境下可以任意运行纯函数(web worker)
- 副作用让函数变得不纯,如果函数依赖于外部的状态就无法保证输出相同,就会带来副作用
- 配置文件,数据库,获取用户输入,等所有的外部交互都会产生副作用
function getSum(n1, n2) {
return n1 + n2
}
console.log(getSum(1, 2))
console.log(getSum(1, 2))
console.log(getSum(1, 2))
4. 高级函数-柯里化Curry
const compose = function (f, g) {
return function (x) {
return f(g(x));
};
}
compose(f, compose(g, h))
// 等同于
compose(compose(f, g), h)
// 等同于
compose(f, g, h)
5. 高级函数-函数组合
- 函数可以看做一个处理数据的管道,管道中输入参数x,在管道中对数据处理后得到结果y
- 通过函数组合可以把多个医院函数组合成一个功能更强大的函数
- 函数组合需要满足结合律
- 函数组合默认的执行顺序是从右向左
function compose(t, f, g) {
return function (value) {
return t(f(g(value)))
}
}
function reverse(array) {
return array.reverse()
}
function first(array) {
return array[0]
}
function toUpper(array) {
return array.toUpperCase()
}
const last = compose(toUpper, first, reverse)
console.log(last(['one', 'two', 'there']))
6. 高级函数-管道
8. 函子
a. 概念:
-
容器:包含值和值的变形关系(这个变形关系就是函数)
-
函数式编程的运算不直接操作值,而是由函子完成
-
函子就是一个实现了map契约的对象
-
我们可以把函子想象成一个盒子,这个盒子里封装一个值
-
想要处理盒子中的值,我们需要给盒子的map方法传递一个处理的函数(纯函数),由这个函数对值进行处理
-
最终map方法返回一个包含新值的盒子(函子)
b: 作用:
9. 函子基本使用-Functor
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(5).map(x => x + 2).map(x => x * x)
console.log(r)
10. 函子空值处理-MayBe
a. 作用:
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('Hello world').map(x => x.toUpperCase())
let r = MayBe.of(null).map(x => x.toUpperCase())
console.log(r)
11. 函子异常处理-Either
class Left {
static of(value) {
return new Left(value)
}
constructor(value) {
this._value = value
}
map(fn) {
return this
}
}
class Right {
static of(value) {
return new Right(value)
}
constructor(value) {
this._value = value
}
map(fn) {
return Right.of(fn(this._value))
}
}
function parseJson(str) {
try {
return Right.of(JSON.parse(str))
} catch (e) {
return Left.of({ error: e.message })
}
}
console.log(parseJson('{name: zhangsan}'))
12. 函子延迟-IO
var fs = require('fs');
var readFile = function(filename) {
return new IO(function() {
return fs.readFileSync(filename, 'utf-8');
});
};
var print = function(x) {
return new IO(function() {
console.log(x);
return x;
});
}
readFile('./user.txt')
.flatMap(print)
13. 函子异步处理-Task
// task
const fs = require('fs')
const { task } = require('folktale/concurrency/task')
const { split, find } = require('lodash/fp')
function readFile(filename) {
return task(resolver => {
fs.readFile(filename, 'utf-8', (err, data) => {
if (err) resolver.reject()
resolver.resolve(data)
})
})
}
readFile('package.json')
.map(split('\n'))
.map(find(x => x.includes('version')))
.run()
.listen({
onRejected: err => {
console.log(err)
},
onResolved: value => {
console.log(value)
}
})
console.log()
14. 函子嵌套-Monad
- Monad内部封装的值是一个函数(这个函数返回函子)
- 目的是通过Join方法避免函子嵌套
const fp = require('lodash/fp')
const fs = require('fs')
class IO {
static of(value) {
return new IO(function () {
return value
})
}
constructor(fn) {
this._value = fn
}
map(fn) {
return new IO(fp.flowRight(fn, this._value))
}
}
let readFile = function (filename) {
return new IO(function () {
return fs.readFileSync(filename, 'utf-8')
})
}
let print = function (x) {
return new IO(function (params) {
console.log(x)
return x
})
}
let cat = fp.flowRight(print, readFile)
let r = cat('package.json')
console.log(r._value()._value())
15. 函数式编程的作用
16. 函数式编程的库
- Lodash---
- Folktale---函子异步处理库
17. 问题总结: