山海科技发展网

08月04日科技常识:JavaScript 函数式编程中 compose 实现

导读 摘要 今天小编跟大家讲解下有关JavaScript 函数式编程中 compose 实现 ,相信小伙伴们对这个话题应该有所关注吧,小编也收集到了有关J...
摘要 今天小编跟大家讲解下有关JavaScript 函数式编程中 compose 实现 ,相信小伙伴们对这个话题应该有所关注吧,小编也收集到了有关JavaScr

今天小编跟大家讲解下有关JavaScript 函数式编程中 compose 实现 ,相信小伙伴们对这个话题应该有所关注吧,小编也收集到了有关JavaScript 函数式编程中 compose 实现 的相关资料,希望小伙伴们看了有所帮助。

简介

比如有这样的需求 要输入一个名字 这个名字有由firstName,lastName组合而成 然后把这个名字全部变成大写输出来 比如输入jack smith我们就要打印出来 ‘HELLO JACK SMITH’。

我们考虑用函数组合的方法来解决这个问题 需要两个函数greeting,toUpper

var greeting = (firstName, lastName) => 'hello, ' + firstName + ' ' + lastNamevar 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.js和ramda.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) }}

这个就是思路 当然这样是不行的 没有退出条件 递归的退出条件就是最后一个函数执行完的时候 也就是count为0的时候 这时候 有一点要注意 递归退出的时候 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]可以看出允许数组为空 可以看出还是非常严谨的。我写的就缺少这种严谨的异常处理。

来源:爱蒂网