函数式编程

“函数式编程”是一种”编程范式”(programming paradigm),也就是如何编写程序的方法论

特点

函数是”第一等公民”

所谓”第一等公民”(first class),指的是函数可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。

1
2
3
4
5
6
var add = function add (a, b) {
return a + b
}
function (a, b, add) {
add(a, b)
}

独立与外界

函数内部逻辑不依赖于外部变量,所有的依赖都通过参数传入(任何时候只要参数相同,函数的返回值总是相同的)。所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。

1
2
3
4
5
6
7
8
9
10
11
12
var a = 1, b = 2;
// 函数编程
function add (a, b) {
return a + b
}
add (a, b)

// 非函数编程,add2的执行依赖于外部变量
function add2 () {
return a + b
}
add2()

只用”表达式”,不用”语句”

  • “表达式”(expression)是一个单纯的运算过程,总是有返回值;
  • “语句”(statement)是执行某种操作,没有返回值。

函数式编程要求,只使用表达式,不使用语句。也就是说,每一步都是单纯的运算,而且都有返回值。

1
2
3
4
5
6
7
// 语句
var a = 1 + 2;
var b = a * 3;
var c = b - 4;

// 表达式
var result = subtract(multiply(add(1,2), 3), 4);

意义

便于重用

由于对每一步运算都进行了封装,而且函数不依赖于外界,所以有很好的复用性。并且,每一个函数都可以被看做独立单元,很有利于进行单元测试(unit testing)和除错(debugging),以及模块化组合。

易于理解

函数式编程的自由度很高,可以写出很接近自然语言的代码。

1
2
3
(1 + 2) * 3 - 4
subtract(multiply(add(1,2), 3), 4)
add(1,2).multiply(3).subtract(4)

表现形式

函数柯里化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 通用的柯里化函数, 实现把所有参数传递给 fn
var currying = function(fn) {
var _args = []; // 用来保存所有的参数
return function _fn (...args) {
if (args.length === 0) return fn.apply(this, _args);
_args.push(...args);
return _fn;
};
};
var multi = (...args) => args.reduce((total, cur) => total + cur, 0);
var sum = currying(multi);
sum(100, 200)(300);
sum(400);
sum(3)(30)(300)(); // 1333

函数的组合

1
2
3
4
5
6
7
8
9
10
11
// 实现把多个函数合成为一个函数
function compose(...funcs) {
if (funcs.length === 0) return a => a
if (funcs.length === 1) return funcs[0]
return funcs.reduce((a, b) => (...args) => a(b(...args)), a => a)
}
var fn1 = (i) => i * 2
var fn2 = (i) => i + 2
var fn3 = (i) => i % 2
const func = compose(fn1,fn2,fn3) // (i) => fn1(fn2(fn3(i)))
func(5) // ((5 % 2) + 2) * 2 = 6