防抖(Debounce)与节流(Throttle)
防抖和节流是两种用于控制函数执行频率的技术,常用于处理高频触发的事件(如输入、滚动、窗口大小改变等),以优化性能。
一、防抖(Debounce)
定义:在事件被触发后,延迟一段时间执行回调。如果在这段时间内再次触发,则重新计时。只有最后一次触发后的延迟时间结束,才会真正执行。
特点:将多次执行合并为最后一次执行。
实现代码
javascript
function debounce(fn, delay) {
let timer = null;
return function(...args) {
// 每次触发都清除之前的定时器
if (timer) clearTimeout(timer);
// 设置新的定时器
timer = setTimeout(() => {
fn.apply(this, args);
timer = null;
}, delay);
};
}
使用示例
javascript
const inputHandler = debounce(() => {
console.log('发送请求');
}, 300);
// 绑定在 input 事件上,用户停止输入 300ms 后才发送请求
适用场景
- 搜索框输入:用户停止输入后才发起搜索请求,避免每次按键都请求。
- 窗口大小调整:调整结束后才重新计算布局,避免频繁重排。
- 表单验证:用户输入完毕后再验证。
- 按钮重复点击:防止短时间内多次提交。
二、节流(Throttle)
定义:在事件被触发后,每隔一段时间执行一次回调。无论触发多频繁,都保证在规定时间间隔内只执行一次。
特点:将高频触发稀释为每隔固定时间执行一次。
实现代码(时间戳版本)
javascript
function throttle(fn, delay) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= delay) {
fn.apply(this, args);
lastTime = now;
}
};
}
实现代码(定时器版本,保证最后一次触发也能执行)
javascript
function throttle(fn, delay) {
let timer = null;
return function(...args) {
if (timer) return;
timer = setTimeout(() => {
fn.apply(this, args);
timer = null;
}, delay);
};
}
更完善的节流(支持首次立即执行和末尾执行) :
javascript
function throttle(fn, delay, options = { leading: true, trailing: true }) {
let timer = null;
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (!lastTime && !options.leading) lastTime = now;
const remaining = delay - (now - lastTime);
if (remaining <= 0) {
if (timer) {
clearTimeout(timer);
timer = null;
}
fn.apply(this, args);
lastTime = now;
} else if (!timer && options.trailing) {
timer = setTimeout(() => {
fn.apply(this, args);
lastTime = options.leading ? Date.now() : 0;
timer = null;
}, remaining);
}
};
}
使用示例
javascript
const scrollHandler = throttle(() => {
console.log('滚动位置更新');
}, 100);
// 滚动时每 100ms 执行一次,不会频繁触发
适用场景
- 滚动加载:滚动到底部加载更多,控制检查频率。
- 鼠标移动/拖拽:实时更新位置,但避免过于频繁。
- 窗口滚动监听:如计算滚动位置、导航高亮等。
- 游戏动画:限制帧率,避免过快执行逻辑。
三、对比与总结
| 特性 | 防抖(Debounce) | 节流(Throttle) |
|---|---|---|
| 执行时机 | 最后一次触发后等待一段时间执行 | 固定时间间隔内最多执行一次 |
| 连续触发 | 会不断推迟执行,可能永不执行(若一直触发) | 按时间间隔稳定执行 |
| 典型场景 | 搜索框、窗口 resize | 滚动、拖拽、鼠标移动 |
| 常用选项 | 立即执行版本(leading) | 首次是否执行(leading)、末尾是否执行(trailing) |
选择原则:
- 如果希望停止操作后才执行 → 用防抖。
- 如果希望持续操作时仍定期执行 → 用节流。
四、注意事项
- 防抖和节流函数返回的新函数,其
this需要正确绑定,实现中已使用apply保留。 - 定时器需要及时清除,避免内存泄漏。
- 实际开发中,可借助 lodash 等库的
_.debounce和_.throttle,但手写实现有助于理解原理。