前言
这是underscore.js源码分析的第六篇,如果你对这个系列感兴趣,欢迎点击
underscore-analysis/ watch一下,随时可以看到动态更新。
下划线中有非常多很有趣的方法,可以用比较巧妙的方式解决我们日常生活中遇到的问题,比如
_.after
,_.before
,_.defer
…等,也许你已经用过他们了,今天我们来深入源码,一探究竟,他们到底是怎么实现的。
指定调用次数(after
, before
)
把这两个方法放在前面也是因为他们俩能够解决我们工作中至少以下两个问题
如果你要等多个异步请求完成之后才去执行某个操作
fn
,那么你可以用_.after
,而不必写多层异步回调地狱去实现需求有一些应用可能需要进行初始化操作而且仅需要一次初始化就可以,一般的做法是在入口处对某个变量进行判断,如果为真那么认为已经初始化过了直接
return
掉,如果为假那么进行参数的初始化工作,并在完成初始化之后设置该变量为真,那么下次进入的时候便不必重复初始化了。
对于问题1
|
|
如果要在任务1,和任务2都结束了才进行fn
任务,我们一般的写法是啥?
可能会下面这样写
|
|
这样确实可以保证任务fn
是在前面两个异步任务都结束之后才进行,但是相信你是不太喜欢回调的写法的,这里举的异步任务只有两个,如果多了起来,恐怕就要蛋疼了。别疼,用下划线的after
函数可以解救你。
|
|
运行截图
有木有很爽,不用写成回调地狱的形式了。那么接下来我们看看源码是怎么实现的。
after源码实现
|
|
源码简单到要死啊,但是就是这么神奇,妥妥地解决了我们的问题1。
对于问题2
|
|
一般需要且只进行一次参数初始化工作的时候,我们可能会像上面那样做。但是其实如果用下划线中的before
方法我们还可以这样做。
|
|
好玩吧,让我们看看_.before
是怎么实现的。
|
|
让函数具有记忆的功能
在程序中我们经常会要进行一些计算的操作,当遇到比较耗时的操作时候,如果有一种机制,对于同样的输入,一定得到相同的输出,并且对于同样的输入,后续的计算直接从缓存中读取,不再需要将计算程序运行那就非常赞了。
举例
|
|
对于上面这个calculate函数,同样的输入1, 2,两次调用的输出都是一样的,并且两次都走了两个耗时的循环,看看下划线中的memoize
函数,如何为我们省去第二次的耗时操作,直接给出300000
的返回值
|
|
源码实现
|
|
相信你已经看懂了源码实现,是不是很简单,但是又很实用有趣。
来一下延时(.delay和.defer)
下划线中在原生延迟函数
setTimeout
的基础上做了一些改造,产生以上两个函数
_.delay(function, wait, *arguments)
就是延迟wait
时间去执行function
,function
需要的参数由*arguments
提供
使用举例
|
|
源码实现
|
|
不过有点需要注意的是_.delay(function, wait, *arguments)``function
中的this
指的是window
或者global
_.defer(function, *arguments)
延迟调用function直到当前调用栈清空为止,类似使用延时为0的setTimeout方法。对于执行开销大的计算和无阻塞UI线程的HTML渲染时候非常有用。 如果传递arguments参数,当函数function执行时, arguments 会作为参数传入
源码实现
|
|
所以主要还是看_.partial
是个啥
可以预指定参数的函数_.partial
局部应用一个函数填充在任意个数的 参数,不改变其动态this值。和bind方法很相近。你可以在你的参数列表中传递_来指定一个参数 ,不应该被预先填充(underscore中文网翻译)
使用举例
|
|
可以看到,我们传入了_
(这里指的是下划线本身)进行占位,后续再讲2和4填充到对应的位置去了。
源码具体怎么实现的呢?
|
|
在上一篇文章如何写一个实用的bind?
有详细讲解,这里我们再回顾一下
executeBound
|
|
先看一下这些参数都�代表什么含义
- sourceFunc:原函数,待绑定函数
- boundFunc: 绑定后函数
- context:绑定后函数this指向的上下文
- callingContext:绑定后函数的执行上下文,通常就是 this
- args:绑定后的函数执行所需参数
这里其实就是执行了这句,所以关键还是如果处理预参数,和后续参数的逻辑
|
|
管道式函数组合
你也许遇到过这种场景,任务A,任务B,任务C必须按照顺序执行,并且A的输出作为B的输入,B的输出作为C的输入,左后再得到结果。用一张图表示如下
那么一般的做法是什么呢
|
|
看起来没有一般的做法那样,层层绕进去了,而是以一种非常扁平的方式使用。
同样我们看看源码是怎么实现的。
_.compose源码
|
|
给多个函数绑定同样的上下文(_.bindAll(object, *methodNames))
将多个函数methodNames绑定上下文环境为
object
😪 😪 😪,好困,写文章当真好要时间和精力,到这里已经快写了3个小时了,夜深,好像躺下睡觉啊!!!啊啊啊,再等等快说完了(希望不会误人子弟)。
|
|
我们用官网给的例子说一下,默认的jQuery中$(selector).on(eventName, callback)
callback中的this
指的是当前的元素本身,当时经过上面的处理,会弹出underscore
。
_.bindAll源码实现
|
|
内部使用了_.bind
进行绑定,如果你对_.bind
原生是如何实现的可以看这里如何写一个实用的bind?
拾遗
最后关于underscore.js中function篇章还有两个函数说一下,另外节流函数
throttle
以及debounce_
会另外单独写一篇文章介绍,欢迎前往underscore-analysis/ watch一下,随时可以看到动态更新。
_.wrap(function, wrapper)
将第一个函数 function 封装到函数 wrapper 里面, 并把函数 function 作为第一个参数传给 wrapper. 这样可以让 wrapper 在 function 运行之前和之后 执行代码, 调整参数然后附有条件地执行.
直接看源码实现吧
|
|
还记得前面说的partial
吧,他会返回一个函数,内部会执行wrapper
,并且func
会作为wrapper
的一个参数被传入。
_.negate(predicate)
将predicate函数执行的结果取反。
使用举例
|
|
看起来好像没什么软用,但是。。。。
|
|
如果要找到奇数呢?
|
|
源码实现
|
|
源码很简单,就是把你传进来的predicate函数执行的结果取反一下,但是应用还是蛮多的。
结尾
这几个是underscore库中function相关的api,大部分已经说完了,如果对你有一点点帮助。
good night 🌙