面向对象编程(OOP)通过封装变化使得代码更易理解。 函数式编程(FP)通过最小化变化使得代码更易理解。 – Michacel Feathers(Twitter)
1、范畴论
函数式编程的起源,是一门叫做范畴论(Category Theory)的数学分支。
理解函数式编程的关键,就是理解范畴论。它是一门很复杂的数学,认为世界上所有的概念体系,都可以抽象成一个个的"范畴"(category)。
1.1、范畴的概念
简单来说,彼此之间存在某种关系的概念、事物、对象等等,都构成"范畴"。随便什么东西,只要能找出它们之间的关系,就能定义一个"范畴"。
1.2、范畴与容器
我们可以把"范畴"想象成是一个容器,里面包含两样东西。
- 值(value)
- 值的变形关系,也就是函数。
下面我们使用代码,定义一个简单的范畴。
class Category{
constructor(val) {
this.val = val
}
add(x){
return x++
}
}
上面代码中,Category是一个类,也是一个容器,里面包含一个值(this.val)和一种变形关系(add)。这里的范畴,就是所有彼此之间相差1的数字。
2、函数的合成
如果一个值要经过多个函数,才能变成另外一个值,就可以把所有中间步骤合并成一个函数,这叫做"函数的合成"(compose)。
下面用一段简单的代码来表示
let compose = function(f, g) {
return function(x) {
g(f(x))
}
}
上面x分别通过函数f
和函数g
的作用。就是x通过f的作用返回y,y再通过g的作用变为z.
3、纯函数
纯函数的定义是,对于相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用,也不依赖外部环境的状态。
let a = 1
let add = (b) => a+b
add(1)
上面的不是纯函数,因为依赖外部变量,输入相同的值不一定输出相同的值。
let a = 1
let add = (b, c) => b+c
add(1, 2)
上面的是一个纯函数,因为该函数不依赖与外部变量,只要输入相同,函数结构不变,得到的结果都是相同的。
4、函数柯里化
所谓"柯里化",就是把一个多参数的函数,转化为单参数函数。这样在函数函数合成的时候就比较方便。
// 柯里化之前
let add = (a,b) => a+b
add(1,2)
// 柯里化之后
let add = (a) => {
return function(b) {
return a + b
}
}
add(1)(2)
事实上柯里化是一种“预加载”函数的方法,通过传递较少的参数,得到一个已经记住了这些参数的新函数,某种意义上讲,这是一种对参数的“缓存”,是一种非常高效的编写函数的方法。