谈谈函数组合式compose及build设计模式

505次浏览

前言

今天主要介绍函数式编程中有一个比较重要的概念就是函数组合(compose),组合多个函数,同时返回一个新的函数。调用时,组合函数按顺序从右向左执行。右边函数调用后,返回的结果,作为左边函数的参数传入,严格保证了执行顺序,这也是compose 主要特点。另外介绍一下build设计模式,大家可以自行体会一下其中的异同。

组合两个函数

compose 非常简单,通过下面示例代码,就非常清楚

function compose (f, g) {
    return function(x) {
        return f(g(x));
    }
}

var arr = [1, 2, 3],
    reverse = function(x){ return x.reverse()},
    getFirst = function(x) {return x[0]},
    compseFunc = compose(getFirst, reverse);

compseFunc(arr);   // 3

组合任意个函数

上面组合了两个函数的compose,也让我们了解了组合的特点,接着我们看看如何组合更多的函数,因为在实际应用中,不会像入门介绍的代码那么简单。

主要注意几个关键点:

利用arguments的长度得到所有组合函数的个数

reduce 遍历执行所有函数。

    var compose = function() {
      var args = Array.prototype.slice.call(arguments);

      return function(x) {
       if (args.length >= 2) {

          return args.reverse().reduce((p, c) => {
            return p = c(p)
         }, x)

       } else {
           return args[1] && args[1](x);
       }
      }
    }

    // 利用上面示例 测试一下。
    var arr = [1, 2, 3],
    reverse = function(x){ return x.reverse()},
    getFirst = function(x) {return x[0]},
    trace = function(x) {  console.log('执行结果:', x); return x}


    compseFunc = compose(trace, getFirst, trace, reverse);

compseFunc(arr);   
 // 执行结果: (3) [3, 2, 1]
 // 执行结果: 3
 // 3

如此实现,基本没什么问题,变量arr 在管道中传入后,经过各种操作,最后返回了结果。

function compose(...funcs) {
  return funcs.reduce((prev, cur) => {
    return (...args) => prev(cur(...args))
  })
}

const toUpperCase = function(x) { return x.toUpperCase(); };
const exclaim = function(x) { return x + '!'; };
var shout = compose(exclaim, toUpperCase);

shout("send in the haorooms");

pipe管道的认识

认识pipe

函数式编程(FP)里面跟compose类似的方法,就是pipe。

pipe,主要作用也是组合多个函数,称之为'流或者管道, 肯定得按照正常方法,从左往右调用函数,与compose 调用方法相反。

ES6 实现Compose function

先看下compose 最基础的两参数版本,

const compose = (f1, f2) => value => f1(f2(value));

利用箭头函数,非常直接的表明两个函数嵌套执行的关系,

接着看多层嵌套。

(f1, f2, f3...) => value => f1(f2(f3));

实现pipe

前面提到pipe 是反向的compose,pipe正向调用也导致它实现起来更容易。

pipe = (...fns) => x => fns.reduce((v, f) => f(v), x)

一行代码就实现了pipe, 套用上面抽象出来的表达式,reduce刚好正向遍历所有函数, 参数x作为传递给函数的初始值, 后面每次f(v)执行的结果,作为下一次f(v)调用的参数v,完成了函数组合调用。

或者,可以把函数组合中,第一个函数获取参数后,得到的结果,最为reduce遍历的初始值。

pipe = (fn,...fns) => (x) => fns.reduce( (v, f) => f(v), fn(x));

利用es6提供的rest 参数 ,用于获取函数的多余参数.提取出第一个函数fn,多余函数参数放到fns中,fns可以看成是数组,也不用像arguments那种事先通过Array.prototype.slice.call转为数组,arguments对性能损耗也可以避免。 fn(x) 第一个函数执行结果作为reduce 初始值。

build模式

下面介绍一下build模式,

build模式案例代码如下:

let Ctx = function (a1, a2, a3) {
    this.a1 = a1;
    this.a2 = a2;
    this.a3 = a3;
}

let CtxBuilder = function () {
    let a1, a2, a3;
    return {
        setA1: function (a1) {
            this.a1 = a1;
            return this;
        },
        setA2: function (a2) {
            this.a2 = a2;
            return this;
        },
        setA3: function (a3) {
            this.a3 = a3;
            return this;
        },
        build: function () {
            return new Ctx(this.a1, this.a2, this.a3)
        }
    }
}

let ctxBuilder = new CtxBuilder().setA1('a1')
    .setA2('a2')
    .setA3('a3');
let ctx = ctxBuilder.build();

大家一看就懂。

小结

上面讲了组合多个函数,及build模式,打包多个函数的方式,这些方式在工作中经常有用到,因此在这里介绍一下。希望对大家有帮助,本文是haorooms博客整理,欢迎关注前端博客,haorooms博客。

相关文章: