防抖,看完就懂了

75 阅读2分钟

什么是防抖(debounce)

存在一个事件,触发该事件n秒之后执行一个回调函数,那么这个事件如果n秒之内再一次被触发则重新计时

怎么解决防抖?

下面我用一个相对简单的方式实现防抖

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>

  <body>
    <input type="text" name="" id="inp" />
    <script>
      // 下面开始实现函数防抖
      let input = document.querySelector('#inp')

      let t = null

      input.oninput = function () {
        if (t !== null) {
          clearTimeout(t)
        }
        t = setTimeout(() => {
          console.log(this.value)
        }, 500)
      }
    </script>
  </body>
</html>

解释

我定义了一个全局变量t,当首次触发oninput事件的时候,t的值为null,不满足if语句,后续执行定时器,后续如果在半秒后没有oninput事件,那么定时器中的回调函数将会被执行。 当我在上一次触发oninput事件后的半秒内又触发了oninput函数,那么此时t的值不为null,满足if语句的条件,这个时候定时器被清除了,之后又重新开始了计时。这样就实现了函数防抖

下面我们升级一下,使用闭包的方式实现函数防抖

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>

  <body>
    <input type="text" name="" id="inp" />
    <script>
      // 下面开始实现函数防抖
      let input = document.querySelector('#inp')

      // 定义一个防抖函数
      let debounce = function (fn, delay) {
        let t = null
        return function () {
          if (t !== null) {
            clearTimeout(t)
          }
          t = setTimeout(() => {
            fn.call(this)
          }, delay)
        }
      }

      input.oninput = debounce(function () {
        console.log(this.value)
      }, 500)
    </script>
  </body>
</html>

解释

我定义了一个函数debounce,该函数返回一个匿名函数,并且返回的函数(内层函数)中使用了debounce外层函数)中的变量——t,形成了闭包。

再触发了oninput事件之后,我执行debounce函数,传入了一个函数500作为防抖函数debounce函数中定时器的回调和延迟时间。这边需要注意的是,debounce函数在执行之后返回的是一个匿名函数,这个匿名函数就是事件所绑定的回调函数。

还需要注意的是,第29行处的console.log(this.value)中的this原本指向的是window,所以需要在第23行处用call进行一个this指向的转换。因为我们在第28行debounce中所传入的函数就是对应第23行处的fn