标签(空格分隔): underscoresegmentfault
本文是underscore源码剖析系列第六篇文章,上节咱们介绍了throttle节流函数的实现,这节将会介绍一下节流函数的兄弟 —— debounce防抖动函数。
throttle函数是在高频率触发的状况下,为了防止函数的频繁调用,将其限制在一段时间内只会调用一次。而debounce函数则是在频繁触发的状况下,只在触发的最后一次调用一次,想像一下若是咱们用手按住一个弹簧,那么只有等到咱们把手松开,弹簧才会弹起来,下面我用一个电梯的例子来介绍debounce函数。app
假如我下班的时候去坐电梯,等了一段时间后,电梯正准备关上门降低,这个时候一个同事走了过来,电梯门被打开,这样电梯就会继续等一段时间,若是中间一直有人进来,那么电梯就一直不会降低,直到最后一我的进来后过了必定时间后尚未下一我的进来,这时电梯才会降低。函数
除了电梯,事实上咱们还有不少应用场景,好比我用键盘不断输入文字,我但愿等最后一次输入结束后才会调用接口来请求展现联想词,若是每次输入一个字的时候就会调用接口,这样调用未免太过于频繁了。this
没有debounce时:spa
有debounce时:3d
知道debounce的工做原理了,咱们能够先本身实现一个比较简单的debounce函数。code
function debounce(func, wait) { var timeout, args, context var later = function() { func.apply(context, args) timeout = context = args = null } return function() { context = this args = arguments // 每次触发都清理掉前一次的定时器 clearTimeout(timeout) // 只有最后一次触发后才会调用later timeout = setTimeout(later, wait) } }
麻雀虽小,五脏俱全,不过这个函数仍是有不少问题,好比每次触发都设置了太多的setTimeout,这样会比较耗费cpu,咱们来看一下underscore的实现方式。blog
// debounce函数传入三个参数,分别是要执行的函数func,延迟时间wait,是否当即执行immediate // 若是immediate为true,那么就会在wait时间段一开始就执行一次func,以后无论触发多少次都不会再执行func // 在相似不当心点了提交按钮两下而提交了两次的状况下颇有用 _.debounce = function (func, wait, immediate) { var timeout, args, context, timestamp, result; var later = function () { // 这个是最关键的一步,由于每次触发的时候都要记录当前timestamp // 可是later是第一次触发后wait时间后执行的,_now()减去第一次触发时的时间固然是等于wait的 // 可是若是后续继续触发,那么_.now() - timestamp确定会小于wait // last是执行later的时间和上一次触发的时间差 var last = _.now() - timestamp; // 若是在later执行前还有其余触发,那么就会从新设置定时器 // last >= 0应该是防止客户端系统时间被调整 if (last < wait && last >= 0) { timeout = setTimeout(later, wait - last); // 若是last大于等于wait,也就是说设置timeout定时器后没有再触发过 } else { timeout = null; // 这个时候若是immediate不为true,就会当即执行func函数,这也是为何immediate为true的时候只会执行第一次触发 if (!immediate) { result = func.apply(context, args); // 解除引用 if (!timeout) context = args = null; } } }; return function () { context = this; args = arguments; // 每次触发都用timestamp记录时间戳 timestamp = _.now(); // 第一次进来的时候,若是immediate为true,那么会当即执行func var callNow = immediate && !timeout; // 第一次进来的时候会设置一个定时器 if (!timeout) timeout = setTimeout(later, wait); if (callNow) { result = func.apply(context, args); context = args = null; } return result; }; };
为了防止出现咱们上面那种不停地设置定时器的状况,underscore只在later函数中在上一次定时器执行结束后才从新设置定时器。
若是传入的immediate为true,那么只会在第一次进来的时候当即执行。很明显在上面代码中func执行只有两处,一个是callNow判断里面,一个是!immediate判断里面,因此这样保证了后续触发不会再执行func。
参考连接:
一、浅谈throttle以及debounce的原理和实现接口