跳到主要内容

函数防抖 (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 次请求