《防抖:给手滑党开的“后悔药”,前端必备防手抖神器!》

84 阅读3分钟

📱 日常生活中的防抖

你有没有这样的经历:

场景一:外卖点单焦虑症
疯狂点击“提交订单”按钮,结果生成了3个相同的订单,还得一个个取消。服务器内心OS:“我只是慢了点,你至于点这么多次吗?”

场景二:搜索框打字狂魔
输入“前端面试题”几个字,每敲一次键盘就搜索一次:

  • “前” - 搜索!
  • “前端” - 搜索!
  • “前端面” - 搜索!
  • ……(服务器已累瘫)

正是因为这样就带来了高并发,增加服务器的压力,就容易导致服务器崩溃,这时候就需要用一个防抖的方法来解决手滑党的老毛病。

防抖

在规定的时间内,没有新的事件触发,才执行,这样就可以有效避免高并发带给服务器的压力,那么话不多说,直接用代码加上解释防抖是如何产生的

<!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>
  <button id="btn">提交</button>

  <script>
    let btn = document.getElementById('btn')

    function handle(e) {   // e 事件参数,默认存在,用来描述这个事件详情
      console.log('提交', e, this);
    }
    btn.addEventListener('click', debounce(handle, 1000))


    function debounce(fn, wait) {
      var timer;
      return function(...arg) {  // 鼠标每次点击触发的是我
        // let _this = this
        clearTimeout(timer)
        timer = setTimeout(() => {
          fn.apply(this, arg)
        }, wait)
      }
    }
    
  </script>
</body>
</html>

首先我在页面写了一个按钮再给按钮绑定一个点击事件来模拟用户点击按钮的场景,那想要实现防抖就需要在这个点击事件里面触发的函数debounce写上防抖代码,而函数里面就需要一个定时器,才能实现倒计时的功能。

 function handle(e) {   // e 事件参数,默认存在,用来描述这个事件详情
      console.log('提交', e, this);
    }
 let btn = document.getElementById('btn')
 btn.addEventListener('click', debounce(handle, 1000))
 
 function debounce(fn,wait){
        clearTimeout(timer)
        setTimeout(() => {
          fn()
        }, wait)
 }

但是这时候新的问题又出现了,我疯狂点提交手都举起来了但是函数还在执行,因为每次点击一次都有一个定时器生成,所以这样就是拖延后面定时器生效。这时候就需要删除上一个的定时器和闭包,这样就可以在最后一次点击完了定时器才开始倒计时,让每次点击这个timer都是上一次点击的timer。

Screenshot_20251202_182318_com_alibaba_android_rimet_ReplayActivity.jpg

 function debounce(fn, wait) {
      var timer;
      return function() {  // 鼠标每次点击触发的是我
        // let _this = this
        clearTimeout(timer)//删除上一次的定时器
        timer = setTimeout(() => {
          fn()
        }, wait)
      }
    }

这时候每次的执行上下文就是这样,这样就让让每次点击这个timer都是上一次点击的timer。

Screenshot_20251202_184807_com_alibaba_android_rimet_ReplayActivity.jpg 但是这时候代码还是有点小问题,因为handle的()里面是可以直接加形参的,addEventListener调用这个handle都会传入一个实参,这时候点击提交会得到一个时间参数

Screenshot_20251202_185639_com_alibaba_android_rimet_ReplayActivity.jpg 这时候就要这样写,在把传入的形参以实参的方式传回fn就是等同于还给了handle

 function debounce(fn, wait) {
      var timer;
      return function(...arg) {  // 鼠标每次点击触发的是我
        // let _this = this
        clearTimeout(timer)
        timer = setTimeout(() => {
          fn.apply(this, arg)
        }, wait)
      }
    }

总结一下
防抖就是给你的代码加了个“脾气”——“别急,等你冷静下来,我马上帮你办!” 🎉

下次遇到手抖的用户,记得给他开这剂“后悔药”💊