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

JavaScript节流的渐进理解

时间:2023-03-27 00:05:29 JavaScript

节流(throttle)是指:事件被频繁触发,但在一段时间内只响应一次。实际应用场景是:我觉得和防抖类似,是高频事件的场景。节流流程:设置一个小周期,如果在这个小周期内有事件触发,无论触发多少次,在周期结束时响应一次就可以了。与防抖对比:防抖是对这波高频事件的最后反应;throttling就是,如果高频事件持续时间长,那么在持续时间内会响应一次或者几次。本文时间线图可以帮助大家理解:js防抖与节流1.setInterval的实现一开始我是比较固执的。由于需要基于一个固定的周期,setIntervel必须全局设置:,function(){flag=true;});timer=setInterval(function(){if(flag==true){flag=false;console.log('你在上一个时段滚动过');}else{//console.log('上一周期无滚动');}},1000);});设置全局setInterval后,每隔1000ms进行一次flag检测:如果flag为true,则表示在前一个1000ms周期内滚动一次或多次,可以执行一个动作(输出“你在上一个周期内滚动”)。如果标志为false,则无需执行操作。为了演示,这里放置了一个else分支,输出了“noscrollingduringlastperiod”。(如果你的浏览器不支持监控documentscoll,试试改成window或document.body;如果1000ms的周期效果不强,试试改成500或100)这里的flag一般称为throttle。写到这里,我想起之前我已经做过一个轮播的节流阀,但是我还是不能立即将思想的转移和代码实现应用到这种常见的节流场景中。2、SetTimeout的实现和封装。为什么说刚刚放弃呢?因为我看到上面文章中的图之后,我认为一定是一个连续的循环。其实大可不必,期间之间可以有间隔,不一定要连续。也就是说,可以使用setTimeout代替setInterval。想象如下过程:(1)第一次触发第一波高频事件,给出响应(设置超时时间,启动周期)。(2)超时时间到,循环结束,执行动作。(3-1)之后,如果仍然连续触发第一波高频事件,则立即响应(设置第二次超时,开始第二次循环);在这种情况下,两次循环之间没有间隔,是连续循环。(3-2)如果第一波高频事件在第一个周期结束前停止,则不会立即设置第二个超时;直到第二波高频事件的第一次触发才会设置第二次超时开始第二个循环;在这种情况下,两个周期之间会有间隙。使用setTimeout代替并封装:完成。大家可以对比一下防抖和节流的代码实现。它们有些相似。区别在于:(1)防抖需要先清除超时再设置超时,不会出现重复超时。(2)throttling就是利用throttle变量来阻断重复超时,超时后会自动完成并清除。三、更多:拆解封装函数和闭包的问题对我来说还是比较陌生的,接触的不多。另外,在throttle()函数中声明了一个flag,是不是每次scroll后都会调用throttle()函数,声明一个新的flag变量?如果同时有很多flag,会很乱,无法正确判断是否执行响应动作。于是尝试拆解封装的函数,发现flag和timer的声明必须放在addEventListener()外面才能成为全局变量。反汇编如下:说明问题:(1)throttle()函数处理scroll的原始handler,相当于修改原始handler,提供一个“增强版”handler一次;this这种返回函数的形式没有重复调用,只调用了一次throttle(),后面的scroll会重复调用“增强版”handler。(这让我一个理解:函数声明本身不是一个对象,而是一组处理参数的语句;constructornew对象和Function对象才是对象)(2)反汇编后,flag和timer一定是写到addEventListener()外面,因为写里面就是写在handler里面,handler会被反复调用,所以flag和timer会被反复声明。(3)封装的throttle()返回一个封装了原始handler的函数。这里flag和timer一定要写在return外面,但是没必要写在throttle()外面作为全局变量;之所以不能写在return里面,是因为如(2),可以写在throttle()里面,因为retutn的函数引用了flag和timer,形成了一个闭包;throttle()没有被多次调用,所以没有声明新的flag和timer,闭包变量也不会被回收,这样就OK了。