在JavaScript前端开发中,频繁触发的事件比如窗口滚动、输入框输入、按钮连续点击等,如果直接绑定处理函数,会导致函数在短时间内被大量调用,不仅浪费计算资源,还可能引发页面卡顿、接口重复请求等问题。防抖和节流就是专门用来解决这类问题的两种核心优化方案,通过控制函数的执行时机和频率,实现性能提升。

防抖的核心原理与实现
防抖的核心思想是:当函数被触发后,不立即执行,而是等待一段时间,如果在这段等待时间内函数再次被触发,就重新计时,直到等待时间结束且没有新的触发,才执行函数。简单来说就是触发频率越高,执行越靠后。
基础防抖实现
基础防抖的实现逻辑是维护一个定时器,每次触发函数时先清除之前的定时器,再重新设置新的定时器,等待指定时间后执行目标函数。
/**
* 基础防抖函数
* @param {Function} fn 需要防抖的目标函数
* @param {number} delay 防抖等待时间,单位毫秒
* @returns {Function} 经过防抖处理的函数
*/
function debounce(fn, delay) {
let timer = null;
return function(...args) {
// 清除之前的定时器
if (timer) {
clearTimeout(timer);
}
// 重新设置定时器
timer = setTimeout(() => {
fn.apply(this, args);
timer = null;
}, delay);
};
}
带立即执行功能的防抖实现
有些场景下我们希望第一次触发函数时立即执行,之后在等待时间内再次触发则不执行,等待时间结束后才能再次触发执行,这就是带立即执行功能的防抖。
/**
* 带立即执行功能的防抖函数
* @param {Function} fn 需要防抖的目标函数
* @param {number} delay 防抖等待时间,单位毫秒
* @param {boolean} immediate 是否立即执行,默认false
* @returns {Function} 经过防抖处理的函数
*/
function debounce(fn, delay, immediate = false) {
let timer = null;
return function(...args) {
if (timer) {
clearTimeout(timer);
}
if (immediate) {
// 判断是否已经有等待执行的定时器,没有则立即执行
const canExecute = !timer;
timer = setTimeout(() => {
timer = null;
}, delay);
if (canExecute) {
fn.apply(this, args);
}
} else {
timer = setTimeout(() => {
fn.apply(this, args);
timer = null;
}, delay);
}
};
}
节流的核心原理与实现
节流的核心思想是:在指定时间内,函数只能执行一次,无论触发多少次,只要时间没到,就不会执行,时间到了就执行一次,然后重新计时。简单来说就是固定时间内只执行一次。
时间戳版节流实现
时间戳版节流通过记录上一次执行的时间,每次触发时判断当前时间和上一次执行时间的差值是否大于等于等待时间,满足则执行函数并更新上一次执行时间。
/**
* 时间戳版节流函数
* @param {Function} fn 需要节流的目标函数
* @param {number} delay 节流间隔时间,单位毫秒
* @returns {Function} 经过节流处理的函数
*/
function throttle(fn, delay) {
let lastTime = 0;
return function(...args) {
const currentTime = Date.now();
// 判断当前时间和上一次执行时间的差值是否大于等于等待时间
if (currentTime - lastTime >= delay) {
fn.apply(this, args);
lastTime = currentTime;
}
};
}
定时器版节流实现
定时器版节流通过维护一个定时器,每次触发时如果定时器不存在就设置定时器,定时器到期后执行函数并清除定时器,等待下一次触发。
/**
* 定时器版节流函数
* @param {Function} fn 需要节流的目标函数
* @param {number} delay 节流间隔时间,单位毫秒
* @returns {Function} 经过节流处理的函数
*/
function throttle(fn, delay) {
let timer = null;
return function(...args) {
if (!timer) {
timer = setTimeout(() => {
fn.apply(this, args);
timer = null;
}, delay);
}
};
}
防抖和节流的实际应用场景
两者的适用场景有明显区别,开发者需要根据实际需求选择:
- 防抖适用场景:输入框搜索联想、表单输入验证、窗口resize事件处理、按钮点击防止重复提交等。比如搜索输入框,用户连续输入时不需要每次输入都请求接口,等用户停止输入一段时间后再请求,减少接口调用次数。
- 节流适用场景:页面滚动加载更多、鼠标移动事件、窗口滚动监听导航栏高亮、拖拽事件等。比如页面滚动时,不需要每次滚动都计算位置,每隔固定时间计算一次即可,避免频繁计算导致的性能问题。
注意事项
在使用防抖和节流时,需要注意以下几点:
- 防抖和节流返回的都是新函数,如果需要取消绑定的事件,要保存返回的函数引用,不能直接使用原函数。
- 如果防抖和节流函数需要传递事件对象,要注意
apply或者call的正确使用,保证事件对象的正常传递。 - 等待时间的选择要根据实际场景调整,时间太短起不到优化效果,时间太长会影响用户体验。
防抖和节流是JavaScript性能优化中非常实用的技巧,理解两者的核心原理和适用场景,能够帮助开发者更合理地控制函数执行频率,提升前端应用的性能和用户体验。
JavaScript防抖节流性能优化debounce_throttle修改时间:2026-07-22 06:51:24