前言
在前端开发中,频繁触发的事件(如 resize、scroll、输入框输入)会导致性能损耗,防抖(Debounce)和节流(Throttle)是解决这类问题的两大核心方案。二者看似相似,实则适用场景和实现逻辑截然不同,本文带你彻底搞懂它们的区别,并手把手实现基础版本。
一、核心定义:一句话分清防抖和节流
防抖(Debounce) :触发事件后,延迟
n秒再执行回调;若在这n秒内再次触发事件,则重新计时,最终仅执行一次。节流(Throttle) :触发事件后,每隔
n秒强制执行一次回调;在这n秒内多次触发,也只会执行一次,核心是「控制执行频率」。
二、防抖(Debounce):像电梯关门一样 “等待最后一人”
1. 通俗理解
防抖的逻辑就像电梯关门:电梯原本计划 3 秒后关门,但若这 3 秒内又有人进入,就会重新计时 3 秒;直到某一次 3 秒内无人进入,电梯才会真正关门。核心是「等待最后一次触发」。
2. 典型应用场景
- 搜索框实时联想:用户连续输入时,避免每输一个字就发请求,等输入停止后再查询;
- 按钮防重复点击:避免用户快速点击提交按钮,导致多次请求接口;
- 窗口 resize/scroll 高频触发:如调整浏览器窗口大小时,等调整停止后再重新计算布局。
3. 基础版防抖函数实现
/**
* 防抖函数
* @param {Function} fn - 需要防抖的回调函数
* @param {number} delay - 延迟执行的时间(毫秒)
* @returns {Function} 包装后的防抖函数
*/
function debounce(fn, delay) {
let timer = null; // 维护一个定时器
// 返回闭包函数,保留 timer 变量的作用域
return function (...args) {
clearTimeout(timer); // 每次触发时,清除之前的定时器
// 重新设置定时器,延迟执行回调
timer = setTimeout(() => {
fn.apply(this, args); // 绑定 this 和参数,保证函数上下文正确
}, delay);
};
}
// 用法示例
function handleResize() {
console.log('页面尺寸已稳定,执行布局计算:', window.innerWidth);
}
// 包装防抖函数,延迟500ms执行
const debounceResize = debounce(handleResize, 500);
// 绑定窗口 resize 事件
window.addEventListener('resize', debounceResize);
三、节流(Throttle):像水龙头一样 “固定频率出水”
1. 通俗理解
节流的逻辑就像调节水龙头:无论你怎么用力按,水龙头都只会每隔 3 秒滴一滴水,不会因为按得频繁就出水更多。核心是「控制执行间隔」,而非等待最后一次触发。
举个更贴近开发的例子:原本页面滚动时每 10ms 触发一次回调,用节流设置 200ms 间隔后,无论滚动多快,都只会每 200ms 执行一次,大幅降低执行频率。
2. 典型应用场景
- 滚动加载:页面滚动时,每隔一段时间判断是否触底,避免高频检测;
- 鼠标移动 / 拖拽:如拖拽元素时,每隔固定时间更新位置,而非实时更新;
- 高频点击按钮:限制按钮点击频率(如秒杀按钮,防止 1 秒内多次点击)。
3. 基础版节流函数实现(时间戳 / 开关法)
/**
* 节流函数(开关法)
* @param {Function} fn - 需要节流的回调函数
* @param {number} interval - 执行间隔(毫秒)
* @returns {Function} 包装后的节流函数
*/
function throttle(fn, interval) {
let isExecutable = true; // 标记是否可执行回调
return function (...args) {
// 若处于“冷却期”,直接返回
if (!isExecutable) return;
// 执行回调,并进入冷却期
isExecutable = false;
fn.apply(this, args); // 绑定 this 和参数
// 间隔时间后,恢复可执行状态
setTimeout(() => {
isExecutable = true;
}, interval);
};
}
// 用法示例
function handleScroll() {
console.log('滚动检测:', window.scrollY);
}
// 包装节流函数,每200ms执行一次
const throttleScroll = throttle(handleScroll, 200);
// 绑定页面滚动事件
window.addEventListener('scroll', throttleScroll);
四、进阶补充(可选)
本文实现的是基础版本的防抖和节流,生产环境中还可优化:
- 防抖支持「立即执行」(第一次触发时直接执行,后续触发延迟);
- 节流支持「时间戳版」(对比开关法,执行时机更精准);
- 增加取消功能(如
debounce.cancel()手动清除定时器); - 兼容
this指向和参数传递(本文已通过apply处理)。
总结
- 防抖:「等最后一次」,适合需要 “操作结束后再执行” 的场景;
- 节流:「控频率」,适合需要 “持续触发但限制执行次数” 的场景;
- 二者都是通过闭包保留变量状态,核心是利用定时器控制函数执行时机。
掌握这两个函数的核心逻辑,能有效解决前端高频事件的性能问题,也是面试中的高频手写考点。