# js 防抖与节流

TIP

在进行窗口的 resize、scroll、oninput、onkeyup、keydown,输入框内容校验等操作时,如果事件处理函数调用的频率无限制,会加重浏览器的负担,导致用户体验非常糟糕。此时我们可以采用debounce(防抖)和throttle(节流)的方式来减少调用频率,同时又不影响实际效果。

控制台查看效果

WARNING

可以发现,一直点击会一直触发事件,如果这个事件为发送一个请求给后台。同样的请求因为用户的多次点击而多次发送请求,返回的结果和只点击一次是一样的,大大加重服务器压力和影响性能。防抖可以解决这样的问题,简单来说,防抖就是通过 setTimeout 的方式,在一定的时间间隔内,将多次触发变成一次触发。

# 函数防抖(setTimeout实现)

TIP

一段时间结束后,才能触发一次事件,如果一段时间未结束,就会重新计算时间

比如乘电梯,门快关了,突然有人要上电梯,电梯并未改变楼层,而是再次打开电梯门。延迟改变楼层的功能,优化了资源。

# 延迟执行

第一种方式为通过设置setTimeout定时器的方式延迟执行,当快速多次点击的时候,每一次都会重置定时器,只有你一段时间都不点击时定时器才能到达条件并执行事 件函数。

TIP

  • 利用闭包的原理形成作用域,使得定时器不会被下一次点击重复或清空。
  • 每次点击都延迟执行。狂点时只有停下后才会执行:
<button type="button" id="btn">快速点击延迟执行</button>
<script>
  var btn1 = document.getElementById("btn");
  // 点击后触发debounce()函数,第一个参数为真实要执行的函数,第二个参数为定时器延迟时间
  btn1.addEventListener("click", debounce(real, 1000));
  function debounce(fn, delay) {
    // 设置time为定时器,初始为 null
    var timer = null;
    // 闭包原理,返回一个函数
    return function() {
      // 如果定时器存在清空定时器
      if (timer) {
        clearTimeout(timer);
      }
      // 设置定时器,规定时间后执行真实要执行的函数
      timer = setTimeout(() => {
        // 将参数传递给真实执行的函数,因为箭头函数继承父级作用域,所以直接...arguments
        fn(...arguments);
      }, delay);
    };
  }
  //真实要执行函数
  function real(e) {
    console.log("快速点击延迟执行");
  }
</script>

# 第一次会执行,后面不执行

因为延迟执行的用户体验不太好,改进一下使得第一次执行,后续不执行

<button id="btn">点击一次立即执行,快速点击第一次&最后一次执行</button>
<script>
    var btn = document.getElementById("btn");
    btn.addEventListener("click", debounce(real, 1000));
    function debounce(fn, delay) {
        var timer = null;
        return function () {
            //定义一个firstClick,判断是否第一次执行,初始值为true
            var firstClick = !timer;
            //第一次会立即执行
            if (firstClick) {
                fn(...arguments);
            }
            // 如果定时器存在清空定时器
            if (timer) {
                clearTimeout(timer);
            }
            // 设置定时器,此时firstClick会变为false,规定时间后time才会为null
            timer = setTimeout(() => {
                timer = null;
            }, delay);
        };
    }
    function real(e) {
        console.log("快速点击的第一次执行,之后不执行");
    }
</script>

# 第一次会执行和最后一次执

使得只点击一次的时候立即执行。当点击多次时第一次和最后一次执行。

    <button id="btn">点击一次立即执行,快速点击第一次&最后一次执行</button>
    <script>
      var btn = document.getElementById("btn");
      // 点击后触发debounce()函数,第一个参数为真实要执行的函数,第二个参数为定时器延迟时间
      btn.addEventListener("click", debounce(real, 1000));
     function debounce(fn, delay) {
       // 设置time为定时器,初始为 null
        var timer = null;
        return function () {
         //定义一个firstClick,判断是否第一次执行,初始值为true
          var firstClick = !timer;
          if (firstClick) {
            fn(...arguments);
          }
          if (timer) {
            clearTimeout(timer);
          }
          timer = setTimeout(() => {
            timer = null;
            // 如果firstClick为true,执行
             if (!firstClick) {
              fn(...arguments);
            }
          }, delay);
        };
      }
      //真实要执行函数
      function fun(e) {
        console.log("点击一次立即执行,点击多次第一次和最后一次执行");
      }
</script>

# 上述三种防抖情况示例

控制台查看效果

# 函数节流

TIP

  • 保证一定时间内,核心代码值执行一次,减少一段时间的触发频率
  • 它与防抖最大的区别就是,无论事件触发多么频繁,都可以保证在规定时间内可以执行一次执行函数

# 利用时间戳实现

狂点不停的话,每隔1秒才执行一次:

<script>
    var btn = document.getElementById("btn");
    // 点击后触发throttle()函数,第一个参数为真实要执行的函数,第二个参数为限制时间
    btn.addEventListener("click", throttle(fun, 1000));
    function throttle(fn, delay) {
        // 设置bef,为上一次执行时间,初始为0
        var bef = 0;
        return function () {
            // 获取当前时间戳
            var now = new Date().getTime();
            //如果当前时间减去上次时间大于限制时间时才执行
            if (now - bef > delay) {
                // 执行函数
                fn(...arguments);
                //执行后,上次时间赋值为这次执行时间
                bef = now;
            }
        };
    }    
    function fun(e) {
        console.log(e.target.innerHTML);
    }
</script> 

# 定时器实现节流

实现第一次点的时候也延迟执行,狂点不停的话,它会每隔一定时间就执行一次。

function throttle(fn, delay) {
    var flag = true;
    return function () {
        // 如果flag为true执行定时器
        if (flag) {
            setTimeout(() => {
                //到规定时间后执行函数,同时 flag = true;
                fn(...arguments);
                flag = true;
            }, delay);
        }
        // flag = false;防止一直执行
        flag = false;
    };
}

# 示例节流

Last Updated: 10/20/2021, 9:47:03 AM