JavaScript函数式编程
函数式编程的基础是一等函数(函数在js中作为一等公民)、作用域(词法作用域,动态作用域)和闭包。
函数式编程的第一个概念是高等函数:高等函数将函数作为参数,或者将函数作为返回值。
高等函数是函数式编程的基础,几乎随处可见高等函数。
由函数构建函数
首先讨论函数的构建。函数式编程通过高等函数和一等函数构建,常用的方法有三种
柯里化
柯里化为每一个逻辑参数返回一个新函数,分为手动柯里化和自动柯里化。
手动柯里化
自己手写函数的柯里化版本,有柯里化方向的选择(向左还是向右)
function div(n ,d) {
return n / d;
}
function leftCurry(n) {
return function (d) {
return n / d;
}
}
function rightCurry(d) {
return function (n) {
return n / d;
}
}
但这样很麻烦,每个函数都要提供一个柯里化版本
自动柯里化
指我们通过一个curry
函数生成一个普通函数的柯里化版本
比如我们可以写这样一个curry2
函数做到手动柯里化中的那种效果
function curry2(func) {
return function (first) {
return function (second) {
return func(first, second);
};
};
}
function div(n, d) {
return n / d;
}
var leftCurry = curry(div);
这样实现的一个leftCurry
跟手动柯里化实现的是一模一样的;但是自动柯里化是用过curry
函数和div
函数构建出leftCurry
函数的;这样做也限定了柯里化的方向;我们可以通过再编写一个curry
函数使用另外一种方向来解决这个问题。
再说一个自动柯里化的用处。这个柯里化如下:
function curry(func) {
return function (args) {
return func(args);
}
}
直观上看这个柯里化有什么用?为什么不直接使用func(args)
呢?
这个柯里化的场景在使用这样的语句时格外有用[11, 11, 11, 11].map(parseInt)
时格外有用。这行代码貌似会返回[11, 11, 11, 11]
,但实际上的返回结果是[11, NaN, 3, 4]
。
这是为什么呢?我们看了map
的源码就能知道,map
接收的函数,实际上是一个iteratee(item, index, array)
,也就是说,这行代码实际运行的是[parseInt(11, 0, array) parseInt(11, 1, array), parseInt(11, 2, array), parseInt(11, 3, array)]
。
为了避免给这个iteratee
传入过多的参数,我们可以通过柯里化返回一个只接受一个参数的函数
[11, 11, 11, 11].parseInt(curry(parseInt))
,这样我们就能得到理想的结果[11, 11, 11, 11]
.
柯里化的缺点
柯里化明显只适合于有限参数的函数;如果函数的参数过多(当然我们杜绝设计这样的函数)或者函数的参数未定,那就不适合用柯里化来构建函数。这时就更适合用partial
。
partial
bind的实现其实就有partial
的写法;因为bind
在传递上下文的时候,也是可以传部分参数的。
Function.prototype.bind = function (context) {
context = Obejct(context) || window;
// 保存一部分参数
var args = [].slice.call(arguments, 1);
// ...其他操作
return function () {
// 补全参数
args = args.concat([].slice.call(arguments));
}
}
compose
compose
就是拼接函数,返回一系列函数组合后的复合函数,好比在数学里, 把函数 f(), g(), 和 h() 组合起来可以得到复合函数 f(g(h()))。
递归
- 递归最有名的应用就是深克隆
- 递归操作都应该被封装
函数式编程的组成
这里有两个React
讲到的概念:纯函数,不变性。
纯函数
什么是纯函数?
- 返回结果只由参数决定,不受外部数据的影响
- 不改变外部状态
- 不受
Math.random
,Date.now
影响,没有this
,没有全局变量
纯函数便于函数的组成,有助于消除函数组合出来新的行为正确与否的担忧:纯函数的组合依旧是纯函数。
不变性
js中的String
类是最适合说不变性的。字符串类型的变量在调用方法后,返回调用后的值,但字符串本身是没有被改变的。
链式调用
链式调用看underscore
中_.chain
的源码就好。