likes
comments
collection
share

节流和防抖(二)

作者站长头像
站长
· 阅读数 18

防抖和节流是针对响应跟不上触发频率这类问题的两种解决方案

防抖表示在用户停止某个操作一段时间之后才执行相应的监听函数,而不是在用户操作的过程当中,浏览器触发多少次事件,就执行多少次监听函数,在当前哪里中加入第三个布尔类型参数来控制在设置时间内只执行第一次或者是最后一次。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>防抖</title>
  </head>
  <body>
    <button id="btn">click</button>
    <script>
      var btn = document.getElementById("btn");
      /**
       * handle 最终需要执行的事件监听
       * wait 事件触发之后多久开始执行
       * immediate 控制执行第一次还是最后一次,false 执行最后一次
       */
      function myDebounce(handle, wait, immediate) {
        // 参数类型判断及默认值处理
        if (typeof handle !== "function") {
          throw new Error("handle must be a function");
        }
        if (typeof wait === "undefined") {
          wait = 300;
        }
        if (typeof wait === "boolean") {
          immediate = wait;
          wait = 300;
        }

        let timer = null;
        return function proxy(...args) {
          let self = this;
          init = immediate && !timer;
          clearTimeout(timer);
          timer = setTimeout(() => {
            timer = null;
            !immediate ? handle.call(self, ...args) : null;
          }, wait);

          // 如果当前传递进来的是 true 就表示我们需要立即执行
          // 如果想要实现只在第一次执行,那么可以添加上 timer 为 null 做为判断
          // 因为只要 timer 为 Null 就意味着没有第二次点击
          init ? handle.call(self, ...args) : null;
        };
      }

      function btnClick(ev) {
        console.log("btn click....", ev.type);
      }

      btn.onclick = myDebounce(btnClick, 200);
    </script>
  </body>
</html>

节流,固定周期内,只执行一次动作,若有新事件触发,不执行。周期结束后,又有事件触发,开始新的周期。这里以滚动条为案例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>节流</title>
  </head>
  <style>
    body {
      height: 5000px;
    }
  </style>
  <body>
    <script>
      function myThrottle(handle, wait) {
        if (typeof handle !== "function") {
          throw new Error("handle must be a function");
        }
        if (typeof wait === "undefined") {
          wait = 400;
        }

        let previous = 0; // 定义变量记录上一次执行时的时间
        let timer = null;

        return function proxy(...args) {
          let now = new Date();
          let self = this;
          let interval = wait - (now - previous);

          if (interval <= 0) {
            // 此时就说明是一个非高频次操作,可以执行 handle
            clearTimeout(timer);
            timer = null;
            handle.call(self, ...args);
            previous = new Date();
          } else if (!timer) {
            // 当我们发现当前系统中有一个定时器了,就意味着我们不需要再开启定时器
            // 此时就说明这次的操作发生在了我们定义的频次时间范围内,那就不应该执行 handle
            // 这个时候我们就可以自定义一个定时器,让 handle 在 interval 之后去执行
            timer = setTimeout(() => {
              clearTimeout(timer); // 这个操作只是将系统中的定时器清除了,但是 timer 中的值还在
              timer = null;
              handle.call(self, ...args);
              previous = new Date();
            }, interval);
          }
        };
      }

      function scrollFn() {
        console.log("scroll...");
      }

      window.onscroll = myThrottle(scrollFn, 500);
    </script>
  </body>
</html>