前端外刊评论 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
文档说过这样句话:
说的很明白了,我们可以使用_.chain
链式调用的同时,也支持JS
原生Array
的prototype
中的push
操作,并且知道你调用value()
这些后面讲,不要耽误大家的思路。
那么这个方法改成这样负责什么职能呢?
就是负责存储报数的同学生成点名单,并不在乎你怎么循环,我只负责保存,所以因为我们为了灵活性
和模块化
吧内部的each
操作剔除了,所以我们必然要在其他地方实现遍历,而且最好能支持自定义循环
...还要能保存上面方法返回的数据...
于是乎我想到了java
中的模板引擎
,比如jsp
,freemaker
等,有这种设置start
和end
以及步长
的操作,通过步长
:1
,2
,-1
,-2
循环,按照这个思路造个支持函数
的轮子,不是刚好可以解决这个问题嘛?
但是我们别着急,若果你了解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循环 )
没让您失望吧,其实对于underscore
和lodash
中的几个重要函数什么防抖
、节流阀
...我想深入开几张讲解的,正好我也深入研究一下,毕竟好姿势
希望大家都能用上。
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) //最终报数名单
完成了
做了什么事?
我们成功把耦合函数内部的循环抽离了,分为了两个函数,
主函数写名单数组,复函数提供喊到
方法并且拿到数组
startInverse
、startRandom
复函数提供方法即可若你需求更加复杂或者更加优雅,你可以把startPositive
也进行抽离,改变步长达到正序
、逆序
、随机
的效果。
① 几乎JS
的书籍都提到这个术语,现在java8都积极响应函数编程,但是有些人依然认为函数思想不如OO思想,评论区撕逼,管他呢,存在即合理。
② 命令编程,机器语言if else
case
控制,流程都是你每行代码进行控制.而函数编程更加自由、优雅。
chain_.chain(obj) 返回一个封装的对象. 在封装的对象上调用方法会返回封装的对象本身, 直道 value 方法调用为止.
这个函数就是为了优雅的使用链式调用准备的,value很简单拿到最终值,毕竟链式调用的原理是返回对象本身,设计模式第一部分我就讲了
underscore
提供一个value
返回真正的数据,并且return
_wrapped
返回原始数据结束tap_.tap(object, interceptor) 用 object作为参数来调用函数interceptor,然后返回object。这种方法的主要意图是作为函数链式调用 的一环, 为了对此对象执行操作并返回对象本身。
可能有些人疑惑?为啥你的
.tap(function (numberBill) {
numberBill
哪来的?
通过源码我们就知道其实是他利用回调将我们传入的[]
返回的。
刚才已经讲了使用方法,涉及到函数编程,我会后期单独出几章,并且仿造轮子
本文github源码有部分注释,搭配看,更加清晰。
看情况百度
其实不懂得东西没那么可怕,我们只有不断学习才能进步,不懂的东西不要百度,尽量看看官网API,看一手资源。
本文 -- 首发github