# 函子
函子(Functor): 用一种函数式的方式帮助我们处理错误。
# 什么是函子
函子是一个普通对象(在其他语言中,可能是一个类),他实现了 map 函数,在遍历每个对象值的时候生成一个新对象。
# 函子是容器
const Container = function (val) {
this.value = val
}
var cont = new Container(1)
// container 持有了内部的值
1
2
3
4
5
6
2
3
4
5
6
为 Container 创建一个名为 of 的静态工具类方法,帮助我们在创建新对象时省略 new 关键字
Container.of = function (val) {
return new Container(val)
}
var test = Container.of(3)
1
2
3
4
5
2
3
4
5
# 函子实现了 map 方法
// map 函数定义
Container.prototype.map = function (fn) {
return Container.of(fn(this.value))
}
1
2
3
4
2
3
4
使用函子:
let double = x => x + x
Container.of(3).map(double).map(double).map(double) // {value: 24}
1
2
3
2
3
# MayBe 函子
它使我们能够以更加函数式的方式处理代码中的错误。
# 实现 MayBe 函子
const MayBe = function (val) {
this.value = val
}
MayBe.of = function (val) {
return new MayBe(val)
}
MayBe.prototype.isNothing = function () {
return (this.value === null || this.value === undefined)
}
MayBe.prototype.map = function (fn) {
return this.isNothing() ? MayBe.of(null) : MayBe.of(fn(this.value))
}
// 这里 map 函数在应用传入的函数之前先使用 isNothing 函数检测容器中的值是否为 null 或 undefined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 使用 MayBe 函子
MayBe.of('string').map(x => x.toUpperCase())
// {valuie: 'STRING'}
1
2
2
上边我们调用 x.toUpperCase() 不需要关心 x 是否是 unll 或者 undefined,如果 string 的值为 null 会如何:
MayBe.of(null).map(x => x.toUpperCase())
// {value: null}
// 代码没有崩溃
1
2
3
2
3
链式调用:
MayBe.of('newming')
.map(x => x.toUpperCase())
.map(x => 'Mr. ' + x)
// 链式调用过程中同样不关心各个中间过程的 null 和 undefined
MayBe.of('newming')
.map(x => undefined)
.map(x => 'Mr. ' + x)
// {value: null}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 真实用例
这个跳过了。。。
# Either 函子
它能够解决分支拓展问题。
例如:
MayBe.of('Geoge')
.map(() => undefined)
.map(x => 'Mr. ' + x)
// MayBe {value: null} // 与预期结果一致,但是我们不知道哪个 map 调用时执行失败了
1
2
3
4
5
2
3
4
5
# 实现 Either 函子
const Nothing = function (val) {
this.value = val
}
Nothing.of = function (val) {
return new Nothing(val)
}
Nothing.prototype.map = function (f) {
return this
}
const Some = function (val) {
this.value = val
}
Some.of = function (val) {
return new Some(val)
}
Some.prototype.map = function (fn) {
return Some.of(fn(this.value))
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
上边两个函数的区别是可以在 Some 上运行函数,而不能在 Nothing 上运行
Some.of('test').map(x => x.toUpperCase())
// Some {value: 'TEST'}
Nothing.of('test').map(x => x.toUpperCase())
// Nothing {value: 'test'}
1
2
3
4
5
2
3
4
5
下面将两个对象封装到 Either 对象中:
const Either = {
Some: Some,
Nothing: Nothing
}
1
2
3
4
2
3
4
这里请查看 p132 的实例