范畴论
函数式编程首先是范畴论的数学分支,认为世界上所有的概念体系都可以抽出一个个范畴。也就是把数学的思维用到开发里边。
彼此之间存在某种关系、事物、对象等等,都构成范畴。任何事物只要找出他们之间的关系,就能定义。
态射用来表示范畴成员之间的关系。范畴论认为,同一个范畴的所有成员,就是不同状态的变形。通过态射一个成员可以形成另一个成员。
从上面图可以看出,成员之间的关系就是箭头,也叫态射(成员变形关系)。通过变形关系一个成员就编程了另一个成员。
一个范畴内所有的成员其实就是一个集合。
变形关系就是一个函数。
也就是说,范畴论是集合论更上层的抽象,简单的理解就是”集合 + 函数”。
没理解?没关系,继续往下看。
我们可以把”范畴”想象成是一个容器,里面包含两样东西。
- 值(成员)
- 值的变形关系(成员变形关系),也就是函数。
通过代码来看:
class Category {
constructor(val){
this.val = val;
}
add(x){
return x + 1;
}
}
这里有一个容器 Category ,他里面有一个值 val ,有一个变形关系 add 。这里的范畴,就是所有彼此之间相差 1 的数字。
基础理论
在数学中,函数的书写形式是这样的:f(x)=y 一个函数 f,x 作为参数并返回输出 y。这里面包含了几个关键点:
- 函数必须接收至少一个参数
- 函数必须返回一个值
- 函数应该是根据接收到的参数 x, 而不是外部定义的 x。
- 只会输出唯一的 y。
通过代码来看:
// 正确示范
function test(x){
return y;
}
// 错误示范
var x = 1;
function test(){
// do something
x = 2;
}
if(a == 1) {
test();
}else{
// ...
}
通过上面代码来看, 有两个示例:首先函数式编程是要通过数学编程的思维。错误的示例里面 if…else 在数学中是不存在的,所以这里第一个错误。第二个错误是函数没有接受参数,上面说了,函数必须接受参数。但是这里并没有,所以这是第二个错误。第三个错误是函数没有返回值,那必须得返回一个值,这里并没有。第四个错误是函数应该是根据接收的参数 x ,而不是直接使用外部的 x。
所以之前的 4 点很重要。
函数式编程并不是用函数编程,也不是传统的面向过程或面对象编程。主要是讲:将复杂的函数复合成简单的函数(计算理论、或者递归论、或者拉姆达运算)。运算过程尽量写成一系列嵌套的函数调用。
**
通过代码来看:
// 一般函数
function add(x,y){
return x + y;
}
// 函数式编程
const compose = function (f, g) {
return function (x) {
return f(g(x));
};
}
看不懂?没关系,先别管这个。就先看两者的区别。运算过程是不是变成了一些系列嵌套的函数调用。在执行 compose 的时候是不是,会返回一个函数。然后再执行, g 执行的结果当成参数传给了 f ,然后 f 执行的结果就会被返回回来。
拆开来看这就是函数的嵌套调用传参返回而已。
在学JavaScript的时候大部分的书中或者老师都会说“函数是一等公民”。所谓第一等公民指的是函数与其他数据类型一样,可以赋值给变量,也可以作为参数传入到另一个函数。或者作为函数的返回值。
在函数式编程中,有一个概念是,不可改变量。也就是变量不能变。因为,在函数式编程中,我们理解的变量会被函数替代,代表了某个表达式。那修改了表达式之后的结果就跟预期所期望的结果就大相径庭了。所有的变量只能被初始赋值一次。map 和 reduce 是最常用的函数式编程的方法。
函数式编程的5个概念:
- 函数是一等公民。
- 只用表达式,不用语句(没有if while这些操作)
- 没有副作用(不能依赖外部变量或者环境,老实干活不依赖任何人)
- 不修改状态(函数同时操作一个变量或者一个函数调用多次一个变量都不会改变该变量的值)
- 引用透明(相同的输入总是获得相同的输出)