函数防抖和节流的理解

函数防抖和节流

引言

引入函数防抖和函数节流的目的是什么呢?

一些场景往往由于事件频繁被触发,因而频繁执行DOM操作、资源加载等重行为,导致UI停顿甚至浏览器崩溃。比如:

  • window对象的resize、scroll事件
  • 拖拽时的mousemove事件射击游戏中的mousedown、keydown事件
  • 文字输入、自动完成的keyup事件

实际上对于window的resize事件,实际需求大多为停止改变大小n毫秒后执行后续处理;而其他事件大多的需求是以一定的频率执行后续处理。针对这两种需求就出现了debounce和throttle两种解决办法。
throttle(又称节流)和debounce(又称去抖)其实都是函数调用频率的控制器。

一.函数防抖(debounce)
  • 防抖的定义:函数防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
  • 让我们来举个例子:函数防抖的实际场景,就像现实生活中我们坐公交,司机需要等最后一个人进入,等待几秒才关门。每次进入一个人,司机就会重新等待几秒再关门,如果在这等待几秒的过程中,又有一个人进入,车不会发动,又会重新等待几秒。直到这几秒内无人再进入后,公交车才发动。
  • 简单来说函数防抖就是当一个事件不断触发时,它的效果函数只执行最后一次。
  • 函数防抖的实现
function debounce(fun,wait){
    
    
        let timer;
        return function(){
    
    
        clearTimeout(timer);
       timer=setTimeout(fun,wait);
      }
  }
  document.addEventListener('scroll', debounce(scrollTap, 500)); 

在这里的scrollTap函数是一个函数名,也就是我们在触发事件后想要进行的效果函数。调用debounce()函数会返回匿名函数,执行此匿名函数的内容。
函数防抖的实现过程就是,一触发事件,debounce函数里就会清除掉之前的定时器,然后再重新设置定时器。当连续触发事件时,就不断的清除定时器,一旦不再触发事件,最后一次触发的事件所绑定的定时器就会执行,延迟时间到达后呈现效果。

二.函数节流(throttle)
  • 节流的定义:限制一个函数在一定时间内只能执行一次。
  • 让我们来举个例子:函数节流的实际场景,就像是设置一个闹铃,让它每隔20分钟执行一次。但也可以让它每隔二十秒响一次,响铃时间为30秒,这样闹铃就不停的响。显然后一种情况是我们并不想要的,前者就像函数节流一样,在一定的时间段内只执行一次。
  • 简单来说函数节流就是当一个事件不断触发时,在一定时间内会执行一次。
  • 函数节流的定时器实现

function throttle(fun,wait){
    
    
      let timer;
      return function(){
    
    
       if(timer){
    
    
        return;
       }
       else{
    
    
        timer=setTimeout(function(){
    
    
         fun();
         timer=null;
        },wait);
       }
      }
  }
  document.addEventListener('scroll', throttle(scrollTap, 500)); 
  

函数节流定时器版的实现,就是当调用throttle函数时,当有定时器在执行时,就直接返回,定时器中的效果函数执行完毕后再重新启动下一次的定时器。这里直接返回达到了延迟事件的目的。

  • 函数节流的时间戳实现
function throttle2(fun,wait){
    
    
      let last=Date.now();
      return function(){
    
    
       let now=Date.now();
       if(now-last>=wait){
    
    
        fun();
        last=now;
       }
       else{
    
    
        return;
       }
      }
     }
     document.addEventListener('scroll', throttle2(scrollTap, 500)); 

函数节流时间戳的实现,就是在通过Date.now()方法创建一个上一次触发事件时间,然后再创建一个当前触发事件的时间,如果这两个的时间间隔不小于延迟时间才执行效果函数,否则不进行任何操作。

三.异同点
异同点 函数防抖 函数节流
相同点 目的都是,降低回调执行频率。节省计算资源。 实现方法都可以用定时器来实现
不同点 侧重一定时间连续触发,只在最后执行一次, 侧重于一段时间内只执行一次。函数节流定时器版中强调的是在一个定时器执行的过程中,不能有别的定时器对它造成干扰。而时间戳版中也是在给定时间内才执行一次方法。也就是说可以有多次的效果函数的实现。
四.小练习

在了解了函数节流和函数防抖方面的内容后,我做了一个比较简单的练习。
练习:想要用四个点击按钮来分别展示添加函数防抖后的效果函数,添加函数节流定时器版后的效果函数,添加函数节流时间戳版的效果函数以及普通的效果函数的不同效果。
我的实现:通过分别给这四个按钮绑定点击事件,然后分别在控制台上打印不同的字样来区分这四种效果。
具体代码如下:

<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title></title>
  <style type="text/css">
   * {
    
    
       padding: 0;
       margin: 0;
   }
   </style>

 </head>
 <body>
   <button style="width:100px;height:50px;">函数防抖定时器</button>
   <button style="width:100px;height:50px;">函数节流定时器</button>
   <button style="width:100px;height:50px;">函数节流时间戳</button>
   <button style="width:100px;height:50px;">这是普通效果!!</button>
      <script>
     var buttonAll=document.getElementsByTagName('button');
     var len=buttonAll.length;
     buttonAll[0].addEventListener('click',debounce(function(){
    
    
      console.log('我是函数防抖');
     },500),false);
     buttonAll[1].addEventListener('click',throttle(function(){
    
    
      console.log('我是函数节流定时器版');
     },500),false);
     buttonAll[2].addEventListener('click',throttle2(function(){
    
    
      console.log('我是函数节流时间戳版');
     },500),false);
     buttonAll[3].addEventListener('click',function(){
    
    
      console.log('我是普通效果');
     },false);
     //函数防抖
     function debounce(fun,wait){
    
    
      let timer;
      return function(){
    
    
       clearTimeout(timer);
       timer=setTimeout(fun,wait);
      }
     }
     //函数节流定时器
     function throttle(fun,wait){
    
    
      let timer;
      return function(){
    
    
       if(timer){
    
    
        return;
       }
       else{
    
    
        timer=setTimeout(function(){
    
    
         fun();
         timer=null;
        },wait);
       }
      }
     }
     //函数节流时间戳
     function throttle2(fun,wait){
    
    
      let last=Date.now();
      return function(){
    
    
       let now=Date.now();
       if(now-last>=wait){
    
    
        fun();
        last=now;
       }
       else{
    
    
        return;
       }
      }
     }
      </script>
 </body>
</html>

五.疑问点

在对整屏翻动进行绑定事件时,由于整屏翻动用的mousewheel事件的效果函数中需要传入参数e(e是事件对象,就是js事件相关信息对象,而且根据不同的事件,属性值会不一样),而e.wheelDelta又是确定鼠标滚动方向的一个属性。这时直接将该效果函数作为形参放入
document.addEventListener(‘mousewheel’, debounce(funOne, 500));
中执行控制台会报错显示wheelDelta有误。这里又要怎么解决呢?

猜你喜欢

转载自blog.csdn.net/qq_44875145/article/details/101477559
今日推荐