欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JS: 防抖节流

程序员文章站 2022-10-31 09:12:45
防抖节流 1. 防抖 (debounce) 先来看看下面的代码: javascript //触发滚动事件,num 就加1 let num = 0; function incNum() { console.log('鼠标滚动中'); console.log( ); } window.addEventL ......

防抖节流

  1. 防抖(debounce)

    先来看看下面的代码:

    //触发滚动事件,num 就加1
    let num = 0;
    
    function incnum() {
     console.log('鼠标滚动中');
        console.log(`${num++} ${date().getseconds()}s`);
    }
    
    window.addeventlistener('scroll', incnum, false);

    当滚动鼠标,num会疯狂加1,从下图中可以看到稍微滚动一下鼠标就输出了n个num

    JS: 防抖节流

    • 什么是防抖?

      防抖指的是让某些代码不可以在没有间断的情况下连续重复地执行。举个例子,一部电梯开门等待30s就会关门上升,在这30s的等待时间里,有人进来了,30s就重新从0开始计时,然后又有人进来了,30s就又重新计时,直到等待时间30s超时才会关门上升。

    • 实现防抖

      以上面的代码为例子来说明如何实现防抖:一直滚动鼠标,第一次触发scroll事件,在事件的执行函数里创建一个定时器,在指定的时间间隔后才运行相关代码。然后第二次触发scroll事件,如果此时前面设置的定时器还没开始执行,就清除前一次的定时器并重新设置一个。这么做的目的就是让scroll事件的执行函数在事件停止触发一段时间后才去执行,以此实现防抖。

      function debounce(func, delay) {
         let timeout;
      
          return function(){
              // context 是为了绑定 this
             let context = this;
              // args 是为了能正常使用事件对象 event
              let args = arguments;
      
              if (timeout) cleartimeout(timeout);
              timeout = settimeout(()=> {
                  func.apply(context, args);
              }, delay);
          }
      }
      
      window.addeventlistener('scroll', debounce(incnum, 1000), false);

      从下面图片可以看出,防抖处理后,只有在鼠标停止滚动一段时间后,才会输出num和秒数。

      JS: 防抖节流

    • 取消执行

      当滚动鼠标后,在1s的等待时间里,突然想取消运行 scroll事件的函数了,该怎么办呢?

      答案是:在定时器没运行前,清除定时器。

      function debounce(func, delay) {
         let timeout;
      
          let debounced = function(){
             let context = this;
              let args = arguments;
      
              if (timeout) cleartimeout(timeout);
              timeout = settimeout(()=> {
                  func.apply(context, args);
              }, delay);
          }
      
          // 取消将要执行的定时器
         debounced.cancel = function() {
              cleartimeout(timeout);
              timeout = null;
              console.log('已取消');
         }
      
         return debounced;
      }
      
      let testfunc = debounce(incnum, 1000);
      window.addeventlistener('scroll', testfunc, false);
      // 在页面的按钮上绑定取消函数
      document.getelementbyid('cancel').addeventlistener('click', testfunc.cancel, false);
    • 立即执行

      只要触发scroll事件,函数就立即执行,但必须要在函数执行完毕并过了一段时间后,再次滚动鼠标才会再次执行函数,而在等待时间里,如果滚动鼠标,等待时间会重新计时。

      function debounce(func, delay, immediate) {
         let timeout;
      
          let debounced = function(){
             let context = this;
              let args = arguments;
      
              if (timeout) cleartimeout(timeout);
              // immediate 为 true 就表示立即执行,忽略或者为 false 即非立即执行
              if (immediate) {
                 let callnow = !timeout;
                  timeout = settimeout(()=> {
                     timeout = null;
                 }, delay);
                  if (callnow) func.apply(context, args);
              } else {
                  timeout = settimeout(()=> {
                     func.apply(context, args);
                 }, delay);
              }
          }
      
          // 取消将要执行的定时器
         debounced.cancel = function() {
              cleartimeout(timeout);
              timeout = null;
              console.log('已取消');
         }
      
         return debounced;
      }
      
      window.addeventlistener('scroll', debounce(incnum, 1000, true), false);
  2. 节流(throttle)

    • 什么是节流?

      还是电梯的例子:一部电梯开门等待30s就会关门上升,在这30s的等待时间里,有人进来了,计时继续累计,然后又有人进来了,计时依然累计,然后30s计时到了就立即关门上升(我还没上车呢.jpg)。

      简单来说,就是连续触发事件,但在固定时间内只执行一次函数。

    • 定时器实现

      // 定时器版本,鼠标滚动1s后才执行函数
      function throttle(func, delay){
          let timeout;
          return function(){
              let context = this;
              let args = arguments;
              if (!timeout) {
                  timeout = settimeout(()=> {
                      func.apply(context, args);
                  }, delay);
              }
          }
      }
      
      window.addeventlistener('scroll', throttle(incnum, 1000), false);

      疯狂滚动鼠标,但从下图可以看出函数只每秒执行一次。

      JS: 防抖节流

    • 时间戳实现

      // 时间戳版本,鼠标滚动函数立即执行,间隔1s再执行下一次
      function throttle(func, delay) {
          let previous = 0;
      
          return function() {
              let now = date.now();
              let args = arguments;
              if ( (now - previous) >= delay ) {
                  func.apply(this, args);
                  previous = now;
              }
          }
      }
      
      window.addeventlistener('scroll', throttle(incnum, 1000), false);
    • 合并实现与取消

      将定时器和时间戳两个版本合并在一起,可以实现滚动鼠标,函数就立即执行,间隔1s再执行下一次,然后停止滚动鼠标后,还会多执行一次的效果。

      function throttle(func, delay) {
          let previous = 0;
          let timeout;
          let now;
      
          let throttled = function() {
              now = date.now();
              let context = this;
              let args = arguments;
              if ( (now - previous) >= delay ) {
                  if (timeout) {
                      cleartimeout(timeout);
                      timeout = null;
                  }
                  previous = now;
                  func.apply(context, args);            
              } else if (!timeout) {
                  timeout = settimeout(()=> {
                      previous = now;
                      timeout = null;
                      func.apply(context, args);
                  }, delay - (now - previous));
              }
          }
      
          // 取消函数
          throttled.cancel = function() {
              cleartimeout(timeout);
              previous = 0;
              timeout = null;
          }
      
          return throttled;
      }
      
      let testfunc = throttle(incnum, 1000);
      window.addeventlistener('scroll', testfunc, false);
      // 在页面的按钮上绑定取消函数
      document.getelementbyid('cancel').addeventlistener('click', testfunc.cancel, false);