开篇
从2015年大学毕业到现在有5年的时间,一直在IT行业工作,由于自己是材料化学专业和计算机专业并没有直接关系,所以计算机相关基础一直比较薄弱。随着工作时间越久,在编程方面越来越深入,越来越感觉自己技术不成体系,最近报了一个前端的进阶班。在今后的学习过程中学到的知识点、工作中的解决方案,将会在这里进行分享。顺便养成自己写博客的良好习惯。
基础篇
函数式编程、异步编程。
- 函数式编程和面向对象编程的关系。
- 函数式编程是指关心数据之间的映射关系。命令行式解决问题的编程方式。
- 面向对象是把数据及对数据的操作方法放在一起,作为一个相互依存的整体对象。
- 函数式编程的基本概念与特性
- 函数是指的数学中的函数,即对数集施加的运算法则
- 函数有相同的输入肯定有相同的输出
- 函数式编程的入门基础
- 函数是一等公民
- 高阶函数
- 闭包
- 常见的函数式编程类库
- lodash/fp
- folktale
函数是一等公民 (eg. 略)
- 函数可以作为变量
- 函数可以作为返回值
- 函数可以作为参数
高阶函数
- 函数可以作为参数
- 函数可以作为返回值
- 高阶函数的意义
- 抽象可以帮我们屏蔽细节,只需要关注我们的目标
- 高阶函数是用来抽象通用的问题
闭包
和GC回收机制有关。网上资料一大把,不多阐述
纯函数
相同的输入永远有相同的输出,并且没有任何可观察的副作用
副作用指的是 函数依赖外部状态,会导致相同的输入有不同的输出。 例如:全局变量、外部配置文件、用户输入的数据、数据库配置等等
- 容易进行测试
- 更容易进行结果缓存
- 可以并行处理
柯里化
柯里化是为了减少外部状态对函数的影响
- 当一个函数有多个参数的时候先传递一部分参数调用它(这部分参数以后永远不变)
- 然后返回一个新的函数接收剩余的参数,返回结果
函数组合
函数组合是为了解决函数嵌套调用的问题。把嵌套调用改写成管道调用。 即:洋葱代码转换为管道代码。就像.net core中的中间件形成的管道。
Point Free
我们可以把数据处理的过程定义成与数据无关的合成运算,不需要用到代表数据的那个参 数,只要把简单的运算步骤合成到一起,在使用这种模式之前我们需要定义一些辅助的基本运算函数。
- 不需要指明处理的数据
- 只需要合成运算过程
- 需要定义一些辅助的基本运算函数
函子
函子的目的是为了把副作用控制在尽可能小的范围内、处理异常、异步操作等。
- Functor
- 容器:包含值和值的变形关系(这个变形关系就是函数)
- 函子:是一个特殊的容器,通过一个普通的对象来实现,该对象具有 map 方法,map 方法可以运 行一个函数对值进行处理(变形关系)
- 函数式编程的运算不直接操作值,而是由函子完成
- 函子就是一个实现了 map 契约的对象
- 我们可以把函子想象成一个盒子,这个盒子里封装了一个值 想要处理盒子中的值,我们需要给盒子的 map 方法传递一个处理值的函数(纯函数),由这 个函数来对值进行处理 最终 map 方法返回一个包含新值的盒子(函子)
// 一个容器,包裹一个值
class Container {
// of 静态方法,可以省略 new 关键字创建对象
static of (value) {
return new Container(value)
}
constructor (value) {
this._value = value
}
// map 方法,传入变形关系,将容器里的每一个值映射到另一个容器
map (fn) {
return Container.of(fn(this._value))
}
}
// 测试
Container.of(3)
.map(x => x + 2)
.map(x => x * x)
- MayBe
- 我们在编程的过程中可能会遇到很多错误,需要对这些错误做相应的处理
- MayBe 函子的作用就是可以对外部的空值情况做处理(控制副作用在允许的范围)
class MayBe { static of (value) { return new MayBe(value) } constructor (value) { this._value = value } // 如果对空值变形的话直接返回 值为 null 的函子 map (fn) { return this.isNothing() ? MayBe.of(null) : MayBe.of(fn(this._value)) } isNothing () { return this._value === null || this._value === undefined } } MayBe.of('Hello World') .map(x => x.toUpperCase()) MayBe.of(null) .map(x => x.toUpperCase())- 在 MayBe 函子中,我们很难确认是哪一步产生的空值问题
- Either
类似与if else 用来处理异常
// 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 })
}
}
// 处理异常
let r = parseJSON('{ name: "zs" }')
.map(x => x.name.toUpperCase())
console.log(r)
- IO 函子
- IO 函子中的 _value 是一个函数,这里是把函数作为值来处理
- IO 函子可以把不纯的动作存储到 _value 中,延迟执行这个不纯的操作(惰性执行)。包装当前的纯操 作
- 把不纯的操作交给调用者来处理
// IO 函子
const fp = require('lodash/fp')
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 r = IO.of(process).map(p => p.execPath)
// console.log(r)
console.log(r._value())
- 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(err)
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)
}
})
- Pointed 函子
- Pointed 函子是实现了 of 静态方法的函子
- of 方法是为了避免使用 new 来创建对象,更深层的含义是 of 方法用来把值放到上下文 Context(把值放到容器中,使用 map 来处理值)
- Monad(单子)
// IO Monad
const fs = require('fs')
const fp = require('lodash/fp')
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))
}
join () {
return this._value()
}
flatMap (fn) {
return this.map(fn).join()
}
}
let readFile = function (filename) {
return new IO(function () {
return fs.readFileSync(filename, 'utf-8')
})
}
let print = function (x) {
return new IO(function () {
console.log(x)
return x
})
}
let r = readFile('package.json')
// .map(x => x.toUpperCase())
.map(fp.toUpper)
.flatMap(print)
.join()
console.log(r)