防抖和节流是前端优化性能、控制事件触发频率的重要方法。以下是手写实现的代码和使用场景。
1. 防抖(Debounce)
防抖的核心思想是:在事件触发后,等待一段时间再执行回调,如果在等待时间内事件又被触发,则重新计时。
实现代码
function debounce(fn, delay = 300) {
let timer = null;
return function (...args) {
const context = this; // 保存 this 指向
if (timer) clearTimeout(timer); // 如果有定时器,清除
timer = setTimeout(() => {
fn.apply(context, args); // 延迟后执行函数
}, delay);
};
}
使用示例
适用于搜索框输入或窗口大小调整:
const inputHandler = debounce(() => {
console.log("执行搜索操作");
}, 500);
document.getElementById("searchInput").addEventListener("input", inputHandler);
2. 节流(Throttle)
节流的核心思想是:在一定时间内,只允许回调函数执行一次。
实现代码(定时器版)
function throttle(fn, delay = 300) {
let timer = null;
return function (...args) {
const context = this; // 保存 this 指向
if (!timer) { // 如果没有定时器
timer = setTimeout(() => {
fn.apply(context, args); // 执行函数
timer = null; // 清空定时器
}, delay);
}
};
}
实现代码(时间戳版)
function throttle(fn, delay = 300) {
let lastTime = 0;
return function (...args) {
const now = Date.now(); // 当前时间
if (now - lastTime >= delay) {
fn.apply(this, args); // 执行函数
lastTime = now; // 更新上次执行时间
}
};
}
使用示例
适用于滚动事件或按钮点击:
const scrollHandler = throttle(() => {
console.log("滚动事件触发");
}, 500);
window.addEventListener("scroll", scrollHandler);
3. 防抖和节流的区别
防抖(Debounce)
定义:事件触发后等待一段时间,若期间有新触发,则重新计时
触发频率:最终触发一次
使用场景:搜索输入、窗口调整等
节流(Throttle)
定义:一段时间内事件只会触发一次
触发频率:固定时间间隔触发一次
使用场景:滚动加载、按钮点击防止多次触发等
4. 合并版:支持立即执行的防抖
有时需要让防抖函数支持立即执行选项:
function debounce(fn, delay = 300, immediate = false) {
let timer = null;
return function (...args) {
const context = this;
if (timer) clearTimeout(timer);
if (immediate && !timer) {
// 立即执行一次
fn.apply(context, args);
}
timer = setTimeout(() => {
fn.apply(context, args);
}, delay);
};
}
5. 合并版:支持立即执行的节流
实现节流时,可以支持“头部触发”和“尾部触发”:
function throttle(fn, delay = 300, options = { leading: true, trailing: true }) {
let timer = null;
let lastTime = 0;
return function (...args) {
const context = this;
const now = Date.now();
if (!options.leading && !lastTime) {
lastTime = now;
}
const remaining = delay - (now - lastTime);
if (remaining <= 0 || remaining > delay) {
if (timer) {
clearTimeout(timer);
timer = null;
}
fn.apply(context, args);
lastTime = now;
} else if (!timer && options.trailing) {
timer = setTimeout(() => {
fn.apply(context, args);
timer = null;
lastTime = options.leading ? Date.now() : 0;
}, remaining);
}
};
}
6. 总结
-
防抖(Debounce) :控制事件只在“空闲时”触发,适合频繁触发的场景。
-
节流(Throttle) :控制事件在“固定间隔”内触发,适合需要平滑触发的场景。 根据实际需求,灵活选择或二次封装即可满足开发场景。