javascript函数式编程系列 ② 优雅的使用underscore进行函数编程

前端外刊评论 2017-10-20

一等公民

函数是一等公民:
所谓一等公民①顾名思义身份高,JS任何只要是值能到达的地方,函数都可以去。

字符串是一等公民,那么函数也可以拥有字符串一样的特质

var str = function(){return '123'} //储存变量

var array = ['123',function(){return '456'}]//做数组成员

var concatStr = '123'+function(){return '456'}() //拼接

function concatFun (str,fn){
    return str + fn();
}
concatFun(123,function(){return '456'}) //做参数

return function(){return '123'} //被返回

正向我们上一文使用call、apply举例,高级函数应该具备的特性:

  • 可以以函数作为参数

  • 可以返回函数

这正是underscore被广泛应用的原因

操场报数游戏

体育老师上课打太极前找同学集合点到:

学生总数10人,报数顺序自行定义:

于是我们写出了如下代码:

(function () {
        var numberBill = [] //报数名单
        for (var number = 0; number < 10; number++) {
            numberBill.push('同学 ' + number + ' 报数');
            if (number == 10) console.log('没有学生了');
            if (number < 10) console.log('还剩:' + (10 - number - 1) + '名学生没有报数');
        }
    })()

结果:

VM85068:6 还剩:9名学生没有报数
VM85068:6 还剩:8名学生没有报数
VM85068:6 还剩:7名学生没有报数
VM85068:6 还剩:6名学生没有报数
VM85068:6 还剩:5名学生没有报数
VM85068:6 还剩:4名学生没有报数
VM85068:6 还剩:3名学生没有报数
VM85068:6 还剩:2名学生没有报数
VM85068:6 还剩:1名学生没有报数
VM85068:6 还剩:0名学生没有报数

这种编程方式很常见,谁都会写,我们一般叫这种编程方式称之为命令编程②,这个逻辑完全在你掌握之中,你只要规定计算机在你想要的时候执行一些不同的动作就可以了。

下文中出现的注解underscore方法会在文章最后注解讲解,复杂的单独抽离几张讲解。

function signInPositive(number) { //正序签到
        var count = 1;
        return _.chain([])//①chain干什么的
                .push('同学 ' + count + ' 正常报数')
                .tap(function (numberBill) { //②numberBill哪来的?
                    _.each(_.range(number),function(v,k){
                        if (count < number) numberBill.push('同学 ' + (count++) + ' 正常报数')
                        console.log('还有'+(number-count)+'名同学没报数');
                    })
                }).value();//③
    }
    signInPositive(10)

虽然我们勉强用函数编程实现了,但是有啥好处呢?没啥好处,最起码这个实现方法没有凸显出函数编程的价值,还不如命令编程容易理解。


我记得去年摩托罗拉出了一个模块化手机,他的摄像头,电池,外壳貌似都能拆卸换配件的,函数式编程也是这样,我们把可以抽象抽象出来,再进行组合,进行模块化


上面的例子循环是从(0--传入的数字)正序循环,若是我不想从0开始呢?若是我倒序循环随机循环呢?

改一下先把_.tap内部的_each循环去掉

function signIn(number) { //签到
        return _.chain([])//①chain干什么的?见文章底部
                /*.push('同学 ' + number + ' 正常报数')*/
                .tap(function (numberBill) { //②numberBill哪来的?见文章底部
                     numberBill.push('同学 ' + (number+1) + ' 正常报数')
                     console.log(('同学 ' + (number+1) + ' 正常报数'))
                    //我故意不在体内循环
                }).value();//③value干啥的?见文章底部
    }

另外说明:

上文中
_.chain([])/*.push('同学 ' + number + ' 正常报数')

这个链式操作加上push是为了让大家理解_.chain函数的作用,因为我在underscore文档说过这样句话:

javascript函数式编程系列 ② 优雅的使用underscore进行函数编程

说的很明白了,我们可以使用_.chain链式调用的同时,也支持JS 原生Arrayprototype中的push操作,并且知道你调用value()这些后面讲,不要耽误大家的思路。

那么这个方法改成这样负责什么职能呢?


就是负责存储报数的同学生成点名单,并不在乎你怎么循环,我只负责保存,所以因为我们为了灵活性模块化吧内部的each操作剔除了,所以我们必然要在其他地方实现遍历,而且最好能支持自定义循环...还要能保存上面方法返回的数据...


于是乎我想到了java中的模板引擎,比如jspfreemaker等,有这种设置startend以及步长的操作,通过步长:12-1-2循环,按照这个思路造个支持函数的轮子,不是刚好可以解决这个问题嘛?

但是我们别着急,若果你了解underscore你会知道,这种东西你找它就好了!

  • 运气不错

哦?运气真好,不小心找到两个函数

javascript函数式编程系列 ② 优雅的使用underscore进行函数编程javascript函数式编程系列 ② 优雅的使用underscore进行函数编程

我们可以用_.range_.reduce搭配使用,用_.range假冒一个数组,搭配reduce迭代,模拟出了一个类似模板引擎步长迭代器之类的东西。
是不是激起了你写JS模板引擎的欲望呢?
--别傻了,js不需要,即便需要类似grunt中的JST插件或者Underscore _.Templates也完全够用了,现在是个公司都搞前后分离模板引擎只适合博客小项目,或者大公司静态化了...

先写个DEMO测试一下:

var sum = _.reduce(_.range(1,10,1), function(memo, num){ return memo + num; }, 0

  这样我们就写出了1-10的累加,连循环都没写

  ps:( 我以前看到个文章:30天不使用for循环 )
  • 欧耶

没让您失望吧,其实对于underscorelodash中的几个重要函数什么防抖节流阀...我想深入开几张讲解的,正好我也深入研究一下,毕竟好姿势希望大家都能用上。

function startPositive(start,end,signInFn){
        return _.reduce(
                _.range(start,end,1),
                function(acc,n){
                    return acc.concat(signInFn(n));
                },[])
    }

    var peapleArr = startPositive(0,10,signIn) //最终报数名单

完成了

做了什么事?

我们成功把耦合函数内部的循环抽离了,分为了两个函数,
主函数写名单数组,复函数提供喊到方法并且拿到数组

  • 好处?
    显而易见,你的喊道方式再也不用修改主函数了,你只要多写几个如:startInversestartRandom复函数提供方法即可

若你需求更加复杂或者更加优雅,你可以把startPositive也进行抽离,改变步长达到正序逆序随机的效果。


① 几乎JS的书籍都提到这个术语,现在java8都积极响应函数编程,但是有些人依然认为函数思想不如OO思想,评论区撕逼,管他呢,存在即合理。

② 命令编程,机器语言if elsecase控制,流程都是你每行代码进行控制.而函数编程更加自由、优雅。


文中出现的underscore函数

  • _.chain
chain_.chain(obj) 
返回一个封装的对象. 在封装的对象上调用方法会返回封装的对象本身, 
直道 value 方法调用为止.

这个函数就是为了优雅的使用链式调用准备的,value很简单拿到最终值,毕竟链式调用的原理是返回对象本身,设计模式第一部分我就讲了

javascript函数式编程系列 ② 优雅的使用underscore进行函数编程javascript函数式编程系列 ② 优雅的使用underscore进行函数编程
粗略通过源码我们知道underscore提供一个value返回真正的数据,并且return_wrapped返回原始数据结束
  • _.tap
tap_.tap(object, interceptor) 
用 object作为参数来调用函数interceptor,然后返回object。这种方法的主要意图是作为函数链式调用 的一环, 为了对此对象执行操作并返回对象本身。

可能有些人疑惑?为啥你的

.tap(function (numberBill) {

numberBill哪来的?

javascript函数式编程系列 ② 优雅的使用underscore进行函数编程

通过源码我们就知道其实是他利用回调将我们传入的[]返回的。

  • _.reduce

刚才已经讲了使用方法,涉及到函数编程,我会后期单独出几章,并且仿造轮子

本文github源码有部分注释,搭配看,更加清晰。

javascript函数式编程系列 ② 优雅的使用underscore进行函数编程
  • 看情况百度
    其实不懂得东西没那么可怕,我们只有不断学习才能进步,不懂的东西不要百度,尽量看看官网API,看一手资源。

  • 本文 -- 首发github

  • 开源交流群:147255248

相关推荐

86447318 / 0评论 2019-10-23