前言
以下是在 JavaScript 中实现防抖(Debounce)和节流(Throttle)的详细解释和代码示例,包括它们的实现思路、使用场景和优化点:
一、防抖(Debounce)
实现思路:
- 当事件触发时,设置一个定时器,在延迟时间内如果再次触发事件,清除之前的定时器并重新设置定时器。
- 只有在延迟时间内没有再次触发事件,定时器到期时才执行目标函数。
function debounce(func, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
// 示例使用
function handleClick() {
console.log('Button clicked');
}
const debouncedClick = debounce(handleClick, 300);
document.getElementById('myButton').addEventListener('click', debouncedClick);
代码解释:
-
function debounce(func, delay) {...}
: -
let timer;
:存储定时器的引用。 -
return function(...args) {...}
:返回一个新的函数,每次调用时清除之前的定时器并设置新的定时器。 -
clearTimeout(timer);
:清除之前的定时器,防止之前的操作执行。 -
timer = setTimeout(() => {...}, delay);
:设置一个新的定时器,在delay
毫秒后执行func
函数。 -
function handleClick() {...}
:定义一个事件处理函数。 -
const debouncedClick = debounce(handleClick, 300);
:创建一个防抖处理后的函数,延迟时间为 300 毫秒。 -
document.getElementById('myButton').addEventListener('click', debouncedClick);
:将防抖处理后的函数添加到事件监听器中。
优化点:
- 可以添加
immediate
参数,决定是否在首次触发时立即执行函数。
function debounce(func, delay, immediate = false) {
let timer;
return function(...args) {
const callNow = immediate &&!timer;
clearTimeout(timer);
timer = setTimeout(() => {
timer = null;
if (!immediate) {
func.apply(this, args);
}
}, delay);
if (callNow) {
func.apply(this, args);
}
};
}
// 示例使用
const debouncedClick = debounce(handleClick, 300, true);
document.getElementById('myButton').addEventListener('click', debouncedClick);
解释:
immediate
参数:如果为true
,则在首次触发时立即执行函数,然后开始防抖。
二、节流(Throttle)
实现思路:
- 记录上次执行的时间,当事件触发时,判断距离上次执行的时间是否超过限制时间。
- 如果超过限制时间,执行函数并更新上次执行时间;如果未超过,不执行。
function throttle(func, limit) {
let inThrottle;
let lastCall = 0;
return function(...args) {
const now = new Date().getTime();
if (now - lastCall < limit) {
return;
}
lastCall = now;
return func.apply(this, args);
};
}
// 示例使用
function handleScroll() {
console.log('Scrolled');
}
const throttledScroll = throttle(handleScroll, 200);
window.addEventListener('scroll', throttledScroll);
代码解释:
-
function throttle(func, limit) {...}
: -
let inThrottle;
和let lastCall = 0;
:存储节流状态和上次执行时间。 -
return function(...args) {...}
:返回一个新的函数,根据当前时间和上次执行时间的差决定是否执行func
。 -
const now = new Date().getTime();
:获取当前时间。 -
if (now - lastCall < limit) {...}
:如果未超过限制时间,不执行函数。 -
lastCall = now;
:更新上次执行时间。 -
return func.apply(this, args);
:执行函数。 -
function handleScroll() {...}
:定义一个事件处理函数。 -
const throttledScroll = throttle(handleScroll, 200);
:创建一个节流处理后的函数,限制时间为 200 毫秒。 -
window.addEventListener('scroll', throttledScroll);
:将节流处理后的函数添加到事件监听器中。
优化点:
- 可以添加
leading
和trailing
参数,决定是否在开始和结束时执行函数。
function throttle(func, limit, leading = true, trailing = true) {
let inThrottle, lastCall, timer;
return function(...args) {
const now = new Date().getTime();
if (!inThrottle) {
if (leading) {
func.apply(this, args);
}
inThrottle = true;
lastCall = now;
} else if (trailing) {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
lastCall = new Date().getTime();
inThrottle = false;
}, limit - (now - lastCall));
}
};
}
// 示例使用
const throttledScroll = throttle(handleScroll, 200, false, true);
window.addEventListener('scroll', throttledScroll);
解释:
leading
参数:如果为true
,在首次触发时立即执行函数。trailing
参数:如果为true
,在最后一次触发后执行函数。
三、总结
- 防抖:适用于需要在事件触发后等待一段时间,等待期间如果再次触发则重新计时的场景,如搜索框输入联想。
- 节流:适用于需要限制事件触发频率的场景,如滚动事件、鼠标移动事件。
- 根据不同的使用场景和需求,可以调整
debounce
和throttle
函数的参数,以达到更好的用户体验和性能优化。
通过使用防抖和节流,可以有效减少函数的执行次数,避免性能问题,尤其是在处理高频率触发的事件时,使代码更加高效和稳定。