JS函数节流和防抖之间的区分和实现详解
在写JS时,这两个函数比较常见,有时候傻傻分不清用哪个,或者说知道代码要怎么写,但要说出它究竟是节流函数还是防抖函数时一脸楞逼。今天有一个同学分享了这两个的区分,我也来回顾一下,加深一下印象,以便日后用到时心里有底。PS:百度和谷歌搜索前几个介绍都是相反介绍,本文为原创,如有雷同纯属抄袭我的。
节流概念(Throttle)
按照设定的时间固定执行一次函数,比如200ms一次。注意:固定就是你在mousemove过程中,执行这个节流函数,它一定是200ms(你设定的定时器延迟时间)内执行一次。没到200ms,一定会返回,没有执行回调函数的。
主要应用场景有:scroll、touchmove
防抖概念(Debounce)
抖动停止后的时间超过设定的时间时执行一次函数。注意:这里的抖动停止表示你停止了触发这个函数,从这个时间点开始计算,当间隔时间等于你设定时间,才会执行里面的回调函数。如果你一直在触发这个函数并且两次触发间隔小于设定时间,则一定不会到回调函数那一步。
主要应用场景有:input验证、搜索联想、resize
节流实现
思路:第一次先设定一个变量true,第二次执行这个函数时,会判断变量是否true,是则返回。当第一次的定时器执行完函数最后会设定变量为flase。那么下次判断变量时则为flase,函数会依次运行。
代码一:首次不执行
functionthrottle(fn,delay=100){ //首先设定一个变量,在没有执行我们的定时器时为null lettimer=null; returnfunction(){ //当我们发现这个定时器存在时,则表示定时器已经在运行中,需要返回 if(timer)return; timer=setTimeout(()=>{ fn.apply(this,arguments); timer=null; },delay); } }
代码二:首次执行
functionthrottle2(fn,delay=100){ letlast=0; returnfunction(){ letcurr=+newDate(); if(curr-last>delay){ fn.apply(this,arguments); last=curr; } } }
防抖实现
思路:首次运行时把定时器赋值给一个变量,第二次执行时,如果间隔没超过定时器设定的时间则会清除掉定时器,重新设定定时器,依次反复,当我们停止下来时,没有执行清除定时器,超过一定时间后触发回调函数。
代码一:首次不执行
functiondebounce(fn,delay=200){ lettimer=null; returnfunction(){ if(timer)clearTimeout(timer); timer=setTimeout(()=>{ fn.apply(this,arguments); timer=null; },delay); } }
代码二:首次执行
functiondebounce2(fn,delay=200,atBegin=true){ lettimer=null,last=0,during; returnfunction(){ letself=this,args=arguments; varexec=function(){ fn.apply(self,args); } if(atBegin&&!timer){ exec(); atBegin=false; }else{ during=Date.now()-last; if(during>delay){ exec(); }else{ if(timer)clearTimeout(timer); timer=setTimeout(function(){ exec(); },delay); } } last=Date.now(); } }
上面的代码只是我自己的一个简单实现,看看lodash里面的两个核心实现代码。生产中建议使用它们的库,毕竟有这么多人在用,出bug的机会比较少,我上面的代码有可能有一些情况没考虑到。如果你发现有问题的,也请告诉我。
如果在项目中有需要用到的,可以直接安装单个的NPM模块。throttle 和 debounce
lodash使用使用文档
lodash库里面这两个函数设置的参数有点复杂,记录一下里面的参数和代码使用。
节流(throttle)
官方文档解释:
创建一个节流函数,在wait秒内最多执行func一次的函数。该函数提供一个cancel方法取消延迟的函数调用以及flush方法立即调用。可以提供一个options对象决定如何调用func方法,options.leading与|或options.trailing决定wait前后如何触发。func会传入最后一次传入的参数给这个函数。随后调用的函数返回是最后一次func调用的结果。
注意:如果leading和trailing都设定为true则func允许trailing方式调用的条件为:在wait期间多次调用。
如果wait为0并且leading为false,func调用将被推迟到下一个点,类似setTimeout为0的超时。
参数
func(Function) 要节流的函数 [wait=0](number) 需要节流的毫秒 [options](Object) 选项对象 [options.leading=true](boolean) 指定调用在节流开始前 [options.trailing=true](boolean) 指定调用在节流结束后
返回值(Function)
返回节流的函数
示例
//避免在滚动时过分的更新定位 jQuery(window).on('scroll',_.throttle(updatePosition,100)); //点击后就调用`renewToken`,但5分钟内超过1次。 varthrottled=_.throttle(renewToken,300000,{'trailing':false}); jQuery(element).on('click',throttled); //取消一个trailing的节流调用 jQuery(window).on('popstate',throttled.cancel);
防抖(debounce)
创建一个debounced(防抖动)函数,该函数会从上一次被调用后,延迟wait毫秒后调用func方法。debounced(防抖动)函数提供一个cancel方法取消延迟的函数调用以及flush方法立即调用。可以提供一个options(选项)对象决定如何调用func方法,options.leading与options.trailing决定延迟前后如何触发(先调用后等待还是先等待后调用)。func调用时会传入最后一次提供给debounced(防抖动)函数的参数。后续调用的debounced(防抖动)函数返回是最后一次func调用的结果。
注意:如果leading和trailing选项为true,则func允许trailing方式调用的条件为:在wait期间多次调用防抖方法。
如果wait为0并且leading为false,func调用将被推迟到下一个点,类似setTimeout为0的超时。
参数
func(Function) 要防抖动的函数 [wait=0](number) 需要延迟的毫秒数 [options](Object) 选项对象 [options.leading=false](boolean) 指定调用在延迟开始前 [options.maxWait](number) 设置func允许被延迟的最大值 [options.trailing=true](boolean) 指定调用在延迟结束后
返回值(Function)
返回具有防抖动功能的函数
示例
//避免窗口在变动时出现昂贵的计算开销。 jQuery(window).on('resize',_.debounce(calculateLayout,150)); //当点击时`sendMail`随后就被调用。 jQuery(element).on('click',_.debounce(sendMail,300,{ 'leading':true, 'trailing':false })); //确保`batchLog`调用1次之后,1秒内会被触发。 vardebounced=_.debounce(batchLog,250,{'maxWait':1000}); varsource=newEventSource('/stream'); jQuery(source).on('message',debounced); //取消一个trailing的防抖动调用 jQuery(window).on('popstate',debounced.cancel);
以上就是这篇节流和防抖的全部介绍,希望对大家的学习有所帮助,也希望大家多多支持毛票票。