FP
Function Programming
本篇文章是自己对于JavaScript函数式编程一书的读书笔记。利用underscore框架介绍几个函数式编程几个概念。
高阶函数
- 将函数作为参数;repeatedly是函数式编程的一个典型思维,将值变成函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14<!--repeatedly值版本-->
function repeatedly(times,value){
return _.map(_.range(times),()=>{ return value })
}
<!--repeatedly 函数版本-->
function repeatedly(times,fun){
return _.map(_.range(times),fun)
}
repeatedly(3,function(){
return Math.floor(Math.random()*10+1)
}) - 返回其他函数的函数。
react和redux里面用了大量的返回其他函数的函数。包括高阶组件,applyMiddleware函数。
Thunk函数也是一个返回其他函数的函数。1
2
3
4
5
6
7
8
9
10
11
12
13var Thunk = function(fn){
return function(){
var args = Array.prototype.slice.call(arguments);
return function(callback){
args.push(callback);
return fn.apply.(this,args);
}
}
}
var readFileThunk = Thunk(fs.readFile);
readFileThunk(fileA)(callback);
由函数构建函数
- 函数式的精华(dispatch函数);dispatch将多个invoker组合在一起,形成多态函数,或根据不同的参数产生不同行为的参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41function existy(x) {
return x != null;
}
function doWhen(cond, action) {
if (tructhy(cond))
return action();
else
return undefined;
}
function invoker(NAME, METHOD) {
return function (target) {
if (!existy(target)) {
alert('Must provide a target');
}
var targetMethod = target[NAME];
var args = _.rest(arguments);
return doWhen(existy(targetMethod) && METHOD === targetMethod, function () {
return targetMethod.apply(target, args);
});
}
}
function dispatch() {
var funs = _.toArray(arguments);
var size = funs.length;
return function (target) {
var ret = undefined;
var args = _.rest(arguments);
for (var funIndex = 0; funIndex < size; funIndex++) {
var fun = funs[funIndex];
ret = fun.apply(fun,construct(target,args))
if(existy(ret)) return ret;
}
if(existy(ret)) return ret;
}
}
var str = dispatch(invoker('toString',Array.prototype.toString),invoker('toString',String.prototype.toString));
console.log(str('a'));
console.log(str(_.range(10))); - 柯里化。
对于每个逻辑参数,柯里化函数会逐渐返回已配置好的函数,直到所有的参数用完。1
2
3
4
5
6
7function curry(fun){
return function(firstArg){
retun function(secondArg){
return fun(firstArg,secondArg);
}
}
} - 部分应用。
柯里化函数逐渐返回消耗参数的函数,直到所有的参数耗尽,然而部分应用函数是一个”部分执行”,等待接收剩余的参数立即执行的参数。1
2
3
4
5function partApply(f, x) {
return function(y) {
return f(x, y);
}
} - 通过组合端至端的拼接函数。
一个理想化的函数式程序是向函数流水线的一端输送的一块数据,从另一端输出一个全新的数据块。
例如: !_isString(name);
- _isString接收一个对象,并返回一个布尔值。
- !接收一个布尔值,并返回一个布尔值。
_.compose函数从右往左执行。也就是说,最右边的函数的结果会被送入其左侧的函数,一个接一个。
1 | function not(x){ |
纯度,不变性和更改政策。
- pure function
- 其结果只能从它的参数的值来计算。
- 不能依赖于能被外部操作改变的数据。
- 不能改变外部状态。
javaScript会导致不纯的函数或方法如Date.now(),console.log(),this和全局变量。
数据的不可变性。(immutablejs);
1
2
3
4
5function touchAndLog(touchFn) {
let data = { key: 'value' };
touchFn(data);
console.log(data.key); // 猜猜会打印什么?
}在不查看 touchFn 的代码的情况下,因为不确定它对 data 做了什么,你是不可能知道会打印什么(这不是废话吗)。但如果 data 是 Immutable 的呢,你可以很肯定的知道打印的是 value。
更改政策。
随便拿到一个对象并改变它,更好的策略是把对象放入容器中,并更改容器。
基于流的编程。
- 惰性链。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23function LazyChain(obj){
this._calls = [];
this._target = obj;
}
LazyChain.prototype.invoke = function(methodName){
var args = _.rest(arguments);
this._calls.push(function(target){
var meth = target[methodName];
return meth.apply(target,args);
})
return this;
}
LazyChain.prototype.force = ()=>{
return _.reduce(this._calls,function(target,thunk){
return thunk(target);
},this._target)
}
new LazyChain([1,2,3]).invoke('sort').force();