一文吃透 JavaScript 防抖:从原理到实战,让你的页面不再 “手抖”

在前端开发中,你一定遇到过这样的场景:用户疯狂点击提交按钮、频繁缩放浏览器窗口、快速输入搜索关键词...... 这些高频触发的事件如果不加控制,会导致页面卡顿、请求泛滥,甚至直接让浏览器崩溃。

这时候,防抖(Debounce) 就是解决这类问题的 "特效药"。今天我们就从 0 到 1,把防抖的原理、实现和实战讲得明明白白。

一、什么是防抖?

防抖的核心思想 :在事件被触发后,延迟一段时间再执行回调函数。如果在这段延迟时间内,事件又被触发了,就重新计时,直到最后一次触发后,延迟时间结束,才真正执行一次函数。

简单来说: "等你消停了,我再干活" 。

二、防抖解决了什么问题?

防抖主要用来优化高频触发事件的性能,常见场景有:

  1. 按钮提交:防止用户重复点击提交按钮,避免重复发送请求。
  2. 搜索框输入:用户输入时,等输入结束后再发送搜索请求,减少请求次数。
  3. 窗口 resize/scroll:避免频繁触发布局计算,提升页面流畅度。
  4. 鼠标移动:防止 mousemove 事件高频触发,降低计算压力。

三、手写一个基础版防抖函数

了解完最基础的是什么和有什么用处后,我们一起来手搓一个防抖函数,先从最简单的版本开始,一步步拆解防抖的实现逻辑。

首先我们先创建一个按钮,按下按钮后会在控制台打印'提交'。

xml 复制代码
 <button id="btn">提交</button>

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

        btn.addEventListener('click', handle)
        function handle() {
            console.log('提交');
        }
     </script>

这样我们就实现了最基础的按下按钮,在控制台打印提交的效果。

为了防止用户频繁重复点击按钮,造成多次重复打印结果,我们手写一个函数debounce来避免这种情况。用户重复点击多次后,只在一秒后执行最后一次点击的打印效果。

将handle函数和时间1s传进去,就可以得到:

xml 复制代码
 <button id="btn">提交</button>

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

        btn.addEventListener('click', debounce(handle,1000))
        function handle() {
            console.log('提交');
        }
     </script>

在'click'后面,我们将debounce函数调用且将handle函数与时间作为参数传了进去,根据addEventListener函数的使用规则,'click'后面应该是一个函数体,而不是函数的调用,所以debounce函数体内必然返回了一个函数。

为了实现用户重复点击多次后,只在一秒后执行最后一次点击的打印效果,我们可以利用setTimeout()、clearTimeout()和闭包的特性来做到。

核心代码:

javascript 复制代码
function debounce(fn, wait) {
            var timer;// 用来存储定时器ID
            return function (...arg) {  // 鼠标每次点击触发的是我
                // 每次触发事件,先清除之前的定时器
                clearTimeout(timer)
                // 重新设置一个新的定时器,延迟 wait 毫秒后执行函数
                timer = setTimeout(() => {
                    // 用 apply 绑定 this 和参数,保证函数执行上下文和参数正确
                    fn.apply(this, arg)
                }, wait)
            }
        }

解析:

  • let timer;:在闭包中声明一个变量,用来保存定时器的 ID,实现 "记忆" 功能。

  • clearTimeout(timer);:每次事件触发,先清除上一次的定时器,相当于 "重新计时"。

  • setTimeout(...):创建新的定时器,延迟 wait 毫秒后执行目标函数 fn。

  • fn.apply(this, args):用 apply 方法,把当前的 this 指向和事件参数传递给原函数,保证函数执行时的上下文和原生事件一致。

效果 :当你疯狂点击按钮时,控制台不会立刻打印 "提交成功",而是会在你最后一次点击后,等待 1 秒才输出一次。完美避免了重复提交!

注意:

  1. this指向问题:防抖函数返回的是一个新函数,如果不用 apply/call 绑定 this,原函数的 this 会指向 window(严格模式下为 undefined),导致业务逻辑出错。
  2. 参数传递问题 :用 ...args 收集所有参数,再通过 apply 传递给原函数,保证事件对象等参数能正常使用。

四、总结

防抖是前端性能优化的基础技能,核心就是 "延迟执行 + 重新计时"。

  • 适用场景:高频触发、只关心最后一次结果的事件(如搜索输入、按钮提交)。
  • 核心优势:减少不必要的函数执行,提升页面性能和用户体验。
  • 实现关键 :利用闭包保存定时器 ID,通过 clearTimeout 和 setTimeout 控制函数执行时机。

下次再遇到用户疯狂点击按钮、频繁输入搜索的场景,别忘了掏出你的防抖 "特效药" 哦!💊

相关推荐
她说可以呀2 小时前
Vue插槽
前端·javascript·vue.js
qq_448011162 小时前
Vue + Jinja2两套模版与法冲突造成undefinedError
前端·javascript·vue.js
aixingpan2 小时前
aixingpan.cn API开发文档:前端查询夏令时和时区接口指南
前端·php
Yuzhiyuxia3 小时前
【AI Agent面试系列】AI Agent记忆机制知识点1
人工智能·面试·职场和发展
2601_958352903 小时前
23.5×19mm 换掉一整条音频链?|A-68 集成了什么:ADC / DAC / 24.576MHz 时钟 / AEC / 波束降噪,主控仅需 6 根线
前端·音视频·ai降噪·回声消除·语音模块
志尊宝3 小时前
AI原生前端开发(039):AI辅助的Nuxt 4全栈开发
前端·vue.js·ai-native
FRing034 小时前
JavaScript入门到精通三十二(深拷贝、浅拷贝、递归函数)
开发语言·前端·javascript
excel4 小时前
双 Token 与前端使用 Crypto 公私钥鉴权换 Token,哪种方案更好?
前端·后端
外收内放4 小时前
Python基础语法练习题(73)
java·前端·python
风骏时光牛马5 小时前
面向复杂场景的大模型应用落地与能力优化实践
前端