函数防抖 (Debounce)
防抖是指在事件被触发后,等待一段时间再执行函数。如果在这段时间内再次被触发,则重新计时。
基础实现
function debounce(fn, delay = 300) {
let timer = null;
return function(...args) {
// 清除之前的定时器
if (timer) {
clearTimeout(timer);
}
// 设置新的定时器
timer = setTimeout(() => {
fn.apply(this, args);
timer = null;
}, delay);
};
}
// 使用
const input = document.querySelector('input');
const handleInput = debounce((e) => {
console.log('输入值:', e.target.value);
}, 500);
input.addEventListener('input', handleInput);
带立即执行选项
function debounce(fn, delay = 300, immediate = false) {
let timer = null;
return function(...args) {
const context = this;
// 清除之前的定时器
if (timer) {
clearTimeout(timer);
}
// 立即执行
if (immediate) {
const callNow = !timer;
timer = setTimeout(() => {
timer = null;
}, delay);
if (callNow) {
fn.apply(context, args);
}
} else {
// 延迟执行
timer = setTimeout(() => {
fn.apply(context, args);
}, delay);
}
};
}
// 使用
// 立即执行,然后等待 500ms
const handleScroll = debounce(() => {
console.log('滚动');
}, 500, true);
取消防抖函数
function debounce(fn, delay = 300) {
let timer = null;
const debounced = function(...args) {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn.apply(this, args);
timer = null;
}, delay);
};
// 添加取消方法
debounced.cancel = function() {
if (timer) {
clearTimeout(timer);
timer = null;
}
};
return debounced;
}
// 使用
const handleResize = debounce(() => {
console.log('窗口大小改变');
}, 300);
window.addEventListener('resize', handleResize);
// 取消
handleResize.cancel();
实际应用场景
搜索框输入
// 搜索框防抖,避免每次输入都请求接口
const searchInput = document.querySelector('#search');
const searchApi = debounce(async (query) => {
const response = await fetch(`/api/search?q=${query}`);
const results = await response.json();
displayResults(results);
}, 500);
searchInput.addEventListener('input', (e) => {
searchApi(e.target.value);
});
窗口调整
// 窗口大小调整时执行
const handleResize = debounce(() => {
console.log('窗口大小:', window.innerWidth, window.innerHeight);
layout.update();
}, 200);
window.addEventListener('resize', handleResize);
表单验证
// 输入框失焦或停止输入后验证
const validateField = debounce((value) => {
if (!value) {
showError('不能为空');
} else if (value.length < 6) {
showError('至少 6 个字符');
} else {
showSuccess('可用');
}
}, 300);
input.addEventListener('input', (e) => {
validateField(e.target.value);
});
防抖 vs 节流
// 防抖:事件停止后才执行
// 适用场景:搜索框、表单验证、自动保存
// 节流:固定时间间隔执行
// 适用场景:滚动加载、按钮点击、窗口调整
// 选择建议:
// - 需要最终结果 → 防抖
// - 需要持续响应 → 节流
性能对比
// 高频事件(如输入)
// 没有防抖:每秒可能触发 10-20 次
// 有防抖:停止输入后 500ms 才执行 1 次
// 减少 API 调用
// 用户输入 "hello" (5 个字符)
// 没有防抖:5 次请求
// 有防抖:1 次请求