当前位置: 首页 > Web前端 > HTML

【跟着大佬学JavaScript】节流

时间:2023-03-28 19:52:13 HTML

前言js的典型场景监听页面的滚动事件,拖动事件监听鼠标的mousemove事件……这些事件会被频繁触发,会影响表现。如果使用节流,会降低频率,保留用户体验,提高执行速度,节省资源。节流的原理是不断触发一个事件,每隔一段时间只执行一次。通俗地说,函数在3秒内被多次调用,但是在3秒的间隔内只执行了一次,第一次执行后3秒内忽略所有后续的函数调用请求,时间间隔不会扩展。3秒间隔结束后,执行一个新的函数调用请求,然后在新的3秒内仍然忽略所有后续的函数调用请求,以此类推。简单的说:每单位时间(3秒),只执行一次。实现方式目前主流的实现方式有两种:timestamp和timer。timestamp实现使用timestamp实现:先将event的执行时间初始化为0,然后上次执行时间减去当前时间戳(now-previous),如果大于wait,直接执行函数,将时间设置为这次执行时间now被分配给previous(previous=now)。由于previous=0是第一次,所以函数会在第一次触发时立即执行。之后,它会在每个等待时间执行。如果触发器停止,函数将不会再次执行。//因为now-0>wait开头,这种写法,时间会立即执行,每秒执行一次。如果触发器停止,事件将不会再次执行functionthrottle(func,wait=500){letcontext,now;让前一个=0;//设置过去的执行时间初始值为0returnfunction(...args){context=this;现在=+(Date.now()||newDate().getTime());if(now-previous>wait){func.apply(context,args);以前=现在;}};}Timer实现使用timer实现:先初始化timeout,然后定义!timeout为true,直接执行setTimeout,等待等待时间后执行函数,然后清除timeout,以此类推,重新进入也会如上执行。由于进入函数后,执行了setTimeout,所以不会立即触发函数执行。之后,它会在每个等待时间执行。如果触发器停止,函数将被再次触发并执行。//由于定时器是在进入时创建的,所以不会立即触发函数执行functionthrottle(func,wait=500){letcontext,timeout;返回函数(...args){context=this;如果(!timeout){timeout=setTimeout(function(){timeout=null;func.apply(context,args);},wait);}};}合并后的版本如果我们需要在start、stop触发后立即执行,并且会触发某个函数的执行。下面,我们结合定时器和时间戳来形成一个新的节流版本。functionthrottle(func,wait=500){让上下文,超时,结果;让前一个=0;constthrottled=function(...args){context=this;constnow=+(Date.now()||newDate().getTime());//当前时间//下一次触发func剩余时间constremaining=wait-(now-previous);//如果没有剩余时间或者系统时间已经改变,此时无需等待,直接立即执行,这样会第一次执行if(remaining<=0||remaining>等待){如果(超时){clearTimeout(超时);超时=空;以前=现在;func.apply(上下文,参数);}elseif(!timeout){//剩下的情况就是remaining<=wait的情况,这里可以用setTimeout最后执行一次timeout=setTimeout(function(){timeout=null;previous=+(Date.now()||newDate().getTime());//这里是重新分配当前时间之前的时间func.apply(context,args);},remaining);}};returnthrottled;}合并版本本次优化没有使用合并版本中的返回值优化+取消功能下面优化代码的返回值+取消函数:functionthrottle(func,wait=500){letcontext,timeout,result;让前一个=0;constshowResult=function(e1,e2){result=func.apply(e1,e2);返回结果;};constthrottled=function(...args){context=this;constnow=+(Date.now()||newDate().getTime());//当前时间//下一次触发func,剩余时间constremaining=wait-(now-previous);//如果没有剩余时间或者系统时间改变了,此时不需要等待,会立即执行,这样会第一次执行if(remaining<=0||剩余>等待){如果(超时){clearTimeout(超时);超时=空;以前=现在;返回显示结果(上下文,参数);}elseif(!timeout){//剩下的情况就是remaining<=wait的情况。这里可以使用setTimeout在结束时执行一次timeout=setTimeout(function(){timeout=null;previous=+(Date.now()||newDate().getT我());//这里是重新赋值之前到当前时间returnshowResult(context,args);},其余的);}返回结果};throttled.cancel=function(){if(timeout!==undefined){clearTimeout(timeout);}前一个=0;上下文=超时=结果=未定义;};functionthrottle(func,wait=500,options={}){让上下文,超时,结果;让前一个=0;//如果同时设置了无头和无尾,则直接使用默认设置,否则按照下面的步骤操作if(!(options.leading===false&&options.trailing===false)){leading=!!options.leading;//删除选项中默认的立即执行部分trailing="trailing"?!!选项。尾随:真;//默认保留尾部}//返回原函数returnconstshowResult=function(e1,e2){result=func.apply(e1,e2);返回结果;};//获取当前时间constgetNow=function(){return+(Date.now()||newDate().getTime());};constthrottled=function(...args){context=this;现在常量=getNow();//当前时间//下次触发func剩余时间if(!previous&&leading===false)previous=now;constremaining=wait-(now-previous);//如果没有剩余时间或者系统时间改变了,这个不需要等待,立即执行,这样if(remaining<=0||remaining&g会第一次执行吨;等待){如果(超时){clearTimeout(超时);超时=空;以前=现在;返回显示结果(上下文,参数);}elseif(!timeout&&trailing!==false){//remaining的情况就是remaining<=wait的情况,这里可以使用setTimeout在最后执行一次timeout=setTimeout(function(){timeout=null;previous=options.leading===false?0:getNow();//这里};throttled.cancel=function(){if(timeout!==undefined){clearTimeout(timeout);}previous=0;context=timeout=result=undefined;};returnthrottled;}这里如果options不传参,函数默认让leading=falselettrailing=true,即无头无尾,如果设置无头无尾同时,then将直接使用默认设置,没有头和尾。//如果同时设置了head和tail,则直接使用默认设置。在其他情况下,将执行以下操作if(!(options.leading===false&&options.trailing===false)){leading=!!options.leading;//删除默认立即执行的部分trailing="trailing"inoptions?!!options.尾随:真;//默认留尾}Demo地址可以去Github仓库查看demo代码跟着大佬学习系列主要是针对日常生活深入了解每个进阶知识点后,跟着大佬深入学习JavaScript的语言艺术。后续会持续更新,希望各位评委不要吝啬手中的好评。??感谢您的支持!!!??如有错误或不准确的地方,请务必指正,万分感谢!!!??如果你喜欢或启发了什么,欢迎star!!!参考JavaScript主题:Learnthrottlingwithunderscoreunderscore.js对节流功能进行了简单的解释。