- 为什么函数式编程?
函数式编程起源于数学中的范畴论。范畴就是使用箭头连接的物体,也就是说,彼此之间存在某种关系的概念、事物、对象等等,都构成"范畴"。随便什么东西,只要能找出它们之间的关系,就能定义一个"范畴"。
上图中,各个点与它们之间的箭头,就构成一个范畴。范畴论认为,同一个范畴的所有成员,就是不同状态的"变形"(transformation)。通过"态射",一个成员可以变形成另一个成员。范畴论使用函数,表达范畴之间的关系。伴随着范畴论的发展,就发展出一整套函数的运算方法。这套方法起初只用于数学运算,后来有人将它在计算机上实现了,就变成了今天的"函数式编程"。
本质上,函数式编程只是范畴论的运算方法,跟数理逻辑、微积分、行列式是同一类东西,都是数学方法,只是碰巧它能用来写程序。
函数式编程可以总结为:
- 与面向对象编程(Object-oriented programming)和过程式编程(Procedural programming)并列的编程范式。
- 最主要的特征是,函数是第一等公民。
- 强调将计算过程分解成可复用的函数,典型例子就是
map方法和reduce方法组合而成 MapReduce 算法。 - 只有纯的、没有副作用的函数,才是合格的函数。
- 怎么使用函数式编程? 函数式编程主要有5个特征
- 不可变性
- 纯函数(pure function)
- 高阶函数
- 柯里化(curry)
- 组合
2.1 不可变性
不可变性指在在函数式编程中,你无法更改数据,也不能更改。 如果要改变或更改数据,则必须复制数据副本来更改。
2.1 纯函数
纯函数指没有副作用的函数,没有副作用就是不会产生对外部可观察的变化,如改变全局变量值 / 对DOM进行修改等等。相同的输入有相同的输出。
2.2 数据转换
JS中不内置的方法不改变原数组,返回新的数组或对象。
concat() join() slice() toString()
2.3 高阶函数
高阶函数指将函数作为参数或者返回函数的函数。
Array.map,Array.filter, Array.reduce等等
2.4 柯里化
curry 的概念:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。
var add = function(x) {
return function(y) {
return x + y;
};
};
var increment = add(1);
var addTen = add(10);
increment(2);
// 3
addTen(2);
// 12
手写一个curry
function curry(){
let args = arguments
return function(){
let args_ = [...args, ...arguments]
console.log(args_.reduce((prev,cur)=>prev+cur,0))
}
}
curry(2)(3,2,3,15)
// result: 25
2.4 代码组合
var compose = function(f,g) {
return function(x) {
return f(g(x));
};
};
上面的代码就是组合,组合看起来像是在饲养函数。你就是饲养员,选择两个有特点又遭你喜欢的函数,让它们结合,产下一个崭新的函数。组合的用法如下:
var toUpperCase = function(x) { return x.toUpperCase(); };
var exclaim = function(x) { return x + '!'; };
var shout = compose(exclaim, toUpperCase);
shout("send in the clowns");
//=> "SEND IN THE CLOWNS!"
手写compose
function compose() {
let func = [...arguments]
return func.reduce((prev, cur) => (...arguments) => {
return cur(prev(...arguments))
})
}
function fn1(x) {
return x + 1;
}
function fn2(x) {
return x + 2;
}
function fn3(x) {
return x + 3;
}
function fn4(x) {
return x + 4;
}
const a = compose(fn1, fn2, fn3, fn4);
console.log(a(22))
// result: 32