资讯专栏INFORMATION COLUMN

关于javascript函数式编程中compose的实现

jonh_felix / 2955人阅读

摘要:结论这次主要介绍了函数式编程中的函数的原理和实现方法,由于篇幅原因,我把打算分析的源码实现放到下一篇来介绍,可以说实现的更加函数式,需要多带带好好分析。

上一篇文章介绍了javascript函数式编程中curry(柯里化)的实现,当然那个柯里化是有限参数的柯里化,等有机会在补上无限参数的那一种柯里化,这次主要说的是javascript函数式编程中另外一个很重要的函数composecompose函数的作用就是组合函数的,将函数串联起来执行,将多个函数组合起来,一个函数的输出结果是另一个函数的输入参数,一旦第一个函数开始执行,就会像多米诺骨牌一样推导执行了。

简介

比如有这样的需求,要输入一个名字,这个名字有由firstName,lastName组合而成,然后把这个名字全部变成大写输出来,比如输入jacksmith我们就要打印出来,‘HELLO,JACK SMITH’
我们考虑用函数组合的方法来解决这个问题,需要两个函数greeting, toUpper

var greeting = (firstName, lastName) => "hello, " + firstName + " " + lastName
var toUpper = str => str.toUpperCase()
var fn = compose(toUpper, greeting)
console.log(fn("jack", "smith"))
// ‘HELLO,JACK SMITH’

这就是compose大致的使用,总结下来要注意的有以下几点

compose的参数是函数,返回的也是一个函数

因为除了第一个函数的接受参数,其他函数的接受参数都是上一个函数的返回值,所以初始函数的参数是多元的,而其他函数的接受值是一元

compsoe函数可以接受任意的参数,所有的参数都是函数,且执行方向是自右向左的,初始函数一定放到参数的最右面

知道这三点后,就很容易的分析出上个例子的执行过程了,执行fn("jack", "smith")的时候,初始函数为greeting,执行结果作为参数传递给toUpper,再执行toUpper,得出最后的结果,compose的好处我简单提一下,如果还想再加一个处理函数,不需要修改fn,只需要在执行一个compose,比如我们再想加一个trim,只需要这样做

var trim = str => str.trim()
var newFn = compose(trim, fn)
console.log(newFn("jack", "smith"))

就可以了,可以看出不论维护和扩展都十分的方便。

实现

例子分析完了,本着究其根本的原则,还是要探究与一下compose到底是如何实现的,首先解释介绍一下我是如何实现的,然后再探求一下,javascript函数式编程的两大类库,lodash.jsramda.js是如何实现的,其中ramda.js实现的过程非常函数式。

我的实现

我的思路是,既然函数像多米诺骨牌式的执行,我首先就想到了递归,下面就一步一步的实现这个compose,首先,compose返回一个函数,为了记录递归的执行情况,还要记录参数的长度len,还要给返回的函数添加一个名字f1

var compose = function(...args) {
    var len = args.length
    return function f1() {
        
    }
}

函数体里面要做的事情就是不断的执行args中的函数,将上一个函数的执行结果作为下一个执行函数的输入参数,需要一个游标count来记录args函数列表的执行情况。

var compose = function(...args) {
    var len = args.length
    var count = len - 1
    var result
    return function f1(...args1) {
        result = args[count].apply(this, args1)
        count--
        return f1.call(null, result)
    }
}

这个就是思路,当然这样是不行的,没有退出条件,递归的退出条件就是最后一个函数执行完的时候,也就是count0的时候,这时候,有一点要注意,递归退出的时候,count游标一定要回归初始状态,最后补充一下代码

var compose = function(...args) {
        var len = args.length
        var count = len - 1
        var result
        return function f1(...args1) {
            result = args[count].apply(this, args1)
            if (count <= 0) {
                count = len - 1
                return result
            } else {
                count--
                return f1.call(null, result)
            }
        }
    }

这样就实现了这个compose函数。后来我发现递归这个完全可以使用迭代来实现,使用while函数看起来更容易明白,其实lodash.js就是这么实现的。

lodash实现

lodash的思路同上,不过是用迭代实现的,我就把它的源代码贴过来看一下

var flow = function(funcs) {
    var length = funcs.length
    var index = length
    while (index--) {
        if (typeof funcs[index] !== "function") {
            throw new TypeError("Expected a function");
        }
    }
    return function(...args) {
        var index = 0
        var result = length ? funcs[index].apply(this, args) : args[0]
        while (++index < length) {
            result = funcs[index].call(this, result)
        }
        return result
    }
}
var flowRight = function(funcs) {
    return flow(funcs.reverse())
}

可以看出,lodash的本来实现是从左到右的,但也提供了从右到左flowRight,还多了一层函数的校验,而且接收的是数组,不是参数序列,而且从这行var result = length ? funcs[index].apply(this, args) : args[0]可以看出允许数组为空,可以看出还是非常严谨的。我写的就缺少这种严谨的异常处理。

结论

这次主要介绍了函数式编程中的compose函数的原理和实现方法,由于篇幅原因,我把打算分析的ramda.js源码实现放到下一篇来介绍,可以说ramda.js实现的compose更加函数式,需要多带带好好分析。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/81571.html

相关文章

  • 翻译连载 | JavaScript轻量级函数编程-第4章:组合函数 |《你不知道JS》姊妹篇

    摘要:把数据的流向想象成糖果工厂的一条传送带,每一次操作其实都是冷却切割包装糖果中的一步。在该章节中,我们将会用糖果工厂的类比来解释什么是组合。糖果工厂靠这套流程运营的很成功,但是和所有的商业公司一样,管理者们需要不停的寻找增长点。 原文地址:Functional-Light-JS 原文作者:Kyle Simpson-《You-Dont-Know-JS》作者 关于译者:这是一个流淌...

    JowayYoung 评论0 收藏0
  • JavaScript 函数编程(二)

    摘要:注意是单一参数柯里化是由以逻辑学家命名的,当然编程语言也是源自他的名字,虽然柯里化是由和发明的。辨别类型和它们的含义是一项重要的技能,这项技能可以让你在函数式编程的路上走得更远。 slide 地址 三、可以,这很函数式~ showImg(https://segmentfault.com/img/remote/1460000015978685?w=187&h=160); 3.1.函数是一...

    thursday 评论0 收藏0
  • 函数编程兴衰与当前之崛起

    摘要:函数式编程逐渐被边缘化,被抛弃到学术界和非主流的场外。组合式编程的重新崛起年左右,有个巨大的变化爆发了。人们开始逐渐在私下里谈论函数式编程。箭头函数对于函数式编程的爆发起到了推动剂的作用。现在很少看到那种不用函数式编程的大型应用了。 showImg(https://segmentfault.com/img/remote/1460000009036867?w=800&h=364); 本...

    binaryTree 评论0 收藏0
  • Js-函数编程

    摘要:组合组合的功能非常强大,也是函数式编程的一个核心概念,所谓的对过程进行封装很大程度上就是依赖于组合。在理解之前,先认识一个东西概念容器容器为函数式编程里普通的变量对象函数提供了一层极其强大的外衣,赋予了它们一些很惊艳的特性。 前言 JavaScript是一门多范式语言,即可使用OOP(面向对象),也可以使用FP(函数式),由于笔者最近在学习React相关的技术栈,想进一步深入了解其思想...

    whinc 评论0 收藏0
  • 使用 javascript 小技巧

    摘要:与条件判断一般我们判断或用如果我们有更多水果与去重提供了新的数据结构。所以所有的关于数据的操作,都可以用函数式的方式处理。这样做的可读性远远高于嵌套一大堆的函数调用我们选择一些函数,让它们结合,生成一个崭新的函数。 1、Array.includes 与条件判断 一般我们判断或用 || // condition function test(fruit) { if (fruit...

    econi 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<