详解Lodash中的fp实现

传统的使用方法:

_.map([1,2,3], (row)=> row*2) //[2,4,6]

fp之后却可以这样用:

fp.map((row)=> row*2)([1,2,3]) //[2,4,6]

这种方式咋一看有些诡异,但如果是比较复杂的数据处理,就会体现出FP的好处:

之前:

var data = _.map([1,2,3], row => row*2)
     .filter( row => row != 2) // [4,6]

之后

var mapMethod = fp.map( row => row * 2 );
var filterMethod = fp.filter( row => row != 2 );
var data = filterMethod(mapMethod([1,2,3])); // [4,6]

或者可以用flow进行组合包装:

var flowMethod = fp.flow( mapMethod, filterMethod );
var data = flowMethod([1,2,3]); //[4,6]

意味着,不应该需要关注具体调用的是哪个方法 ,而应该每个方法可以很轻易地组合与调整,只需要最后传入数据即可。因为对于代码消费者而言, _.map, filter这些方法是他不应该关心的,而如果想调整顺序,比如先filter再map,就会带来很多的麻烦,需要去修改源码。

lodash中的fp有何特性?

其核心特性就三个:自动curry化,迭代优先数据置后,immutable。

自动curry化(柯里化):可以参照 _.curry 的说明, 主要是可以转换函数调用方式, 很方便地生成新函数:

 

var abc = function(a,b,c){ return [a,b,c]; }
var curried = _.curry(abc);
curried(1)(2)(3) // -> [1,2,3]
curried(1,2)(3) // -> [1,2,3]
//而在fp中自动就有这个行为, 比如_.delay默认需要2个参数,则会能这样调用:
fp.delay(1500)(()=> console.log('delay')) 

咦,调用方式有点奇怪,delay的文档里写的是_.delay(func, wait, [args]),为什么会先传入wait了?其实,这就是迭代优先数据置后的效果。

immutable这个有点费解,跟immutablejs似乎不是一回事,他解决的是传入的函数被修改的问题:

 

var mapMethod = (row)=> row*2;
var process = (data)=> _.map(data, mapMethod);
mapMethod = (row)=> row*3;
process([1,2,3]) // -> [3,6,9]; 传统方式下其调用的函数被修改了

var mapMethod2 = (row)=> row*2;
var process2 = fp.map(mapMethod2);
mapMethod2 = (row)=> row*3;
process2([1,2,3]) // -> [2,4,6]; fp的方式下已经传入的函数不会被修改

与传统的比较

传统方式当然也有办法,那就是封装为类与对象。比如上面的处理,用类与对象的方式就可以这样:

class Dealer {
     constructor(val){ this.val = val; } 
     map(){ this.val = _.map(this.val, row => row*2); return this; }
     filter(){ this.val = _.map(this.val, row => row !=2 ); return this; }
     value(){ return this.val; }
} 
var dealer = new Dealer([1,2,3]); 
var data = dealer.map().filter().value();

当然也不错,不过,代码多了好多呀,而且如果想扩展怎么办?需要用类的继承,或是原型继承。

class Dealer2 extends Dealer { ... } // 类的继承
Dealer1.prototype.newMethod = function(){}... //原型继承

但如果是fp的方式呢?直接再调用另一个函数就好了,至于函数从哪里来,没所谓。数据的处理变得非常地简单直接:输出一个数据,经过一系列函数的处理,输出另一个数据。

那么,fp是银弹么?能解决所有的问题?也不尽然。

fp用于数据处理的组合扩展方面非常强大,但如果涉及到状态管理,似乎就依然是类与对象会更加合适一些。

比如大名鼎鼎的redux,几乎全程fp,但却依然有至少一个对象: store 对象,里面有着4个方法: getState(), dispatch(action), subscribe(listener), replaceReducer(nextReducer)。

嗯, Dan Abramov其实还是很懂fp的短板的嘛。不过再看下redux中的createStore源码,其实还是满满的fp风格, 其对象创建大概是这样滴:

export default function createStore(...){
    var currentState = ...
    // more initialize codes...

    function getState(){ return currentState } 
    function subscribe(listener) { ... }
    function dispatch(action) { ... }
    function replaceReducer(nextReducer) { ... }

    return {
        dispatch,
        subscribe,
        getState,
        replaceReducer
    }
}

你可能感兴趣的:(JavaScript)