防抖是事件停止触发后延迟执行一次,适用于搜索输入、窗口resize等场景;节流是固定时间间隔内最多执行一次,适用于滚动加载、鼠标移动等高频持续触发场景。两者均通过定时器控制执行频率,解决高频事件导致的性能问题,核心在于合理选择应用场景并处理this指向、参数传递及执行时机等问题。

JavaScript 中的防抖(Debounce)与节流(Throttle)是两种核心的性能优化策略,它们通过控制高频事件回调函数的执行频率,有效避免了因事件密集触发导致的浏览器性能下降、资源浪费甚至页面卡顿。简单来说,防抖是“你尽管触发,我只在你停止触发一段时间后执行一次”,而节流则是“你尽管触发,我保证在一段时间内最多只执行一次”。这两种机制就像是给事件处理函数加了“限速器”或““冷却时间”,确保了用户体验的流畅性。
理解防抖和节流的实现原理,其实就是理解如何用定时器(
setTimeout
防抖(Debounce)的实现
防抖的核心思想是:当事件被触发时,不立即执行回调函数,而是设置一个定时器。如果在定时器设定的时间间隔内,事件再次被触发,那么就清除上一个定时器,并重新设置一个新的定时器。只有当事件在指定时间间隔内没有再次被触发时,回调函数才会被执行。
我个人觉得防抖就像是你在等电梯,如果不断有人按楼层,电梯就一直开着门等你,直到没人按了,它才真正关门上行。这种等待机制在很多交互场景下简直是救星。
function debounce(func, delay) {
let timeoutId; // 用于存储定时器ID
return function(...args) {
const context = this; // 保存函数执行时的this上下文
clearTimeout(timeoutId); // 每次事件触发都清除上一个定时器
timeoutId = setTimeout(() => {
func.apply(context, args); // 在延迟后执行函数,并传递正确的this和参数
}, delay);
};
}
// 示例用法:
// const myEfficientFn = debounce(() => console.log('我被执行了!'), 500);
// window.addEventListener('resize', myEfficientFn); // 窗口大小调整停止500ms后才执行节流(Throttle)的实现
节流的核心思想是:在指定的时间间隔内,无论事件被触发多少次,回调函数都只会被执行一次。它保证了函数执行的频率上限。
节流则更像是坐公交车,每隔固定时间发一班,不管这期间有多少人在等,到了点就走。它保证了执行频率的上限,不会让事件处理彻底停滞,又能避免资源过度消耗。
function throttle(func, delay) {
let inThrottle = false; // 标记是否在节流期内
let timeoutId;
return function(...args) {
const context = this;
if (!inThrottle) {
func.apply(context, args); // 立即执行一次
inThrottle = true;
timeoutId = setTimeout(() => {
inThrottle = false; // 延迟结束后,重置标记,允许下次执行
// 如果有最后一次触发的事件,且在节流期内,可以考虑在这里执行一次,实现“尾部触发”
// 但基础的节流通常不包含这个,需要额外逻辑处理
}, delay);
}
};
}
// 示例用法:
// const myEfficientScroll = throttle(() => console.log('滚动事件被节流了!'), 200);
// window.addEventListener('scroll', myEfficientScroll); // 滚动事件每200ms最多执行一次上面这个节流实现是“立即执行”版本,即在节流期开始时立即执行一次。还有一种常见的“延迟执行”版本,即在节流期结束后才执行。实际应用中,通常会结合使用或者根据需求选择。
我们经常会遇到用户疯狂滚动页面、快速输入搜索词或者拖拽元素的情况。如果不加限制,每次事件触发都执行回调,浏览器就得拼命工作,轻则卡顿,重则直接崩溃。这就像你给一个服务器发了成千上万个请求,它当然受不了。
高频事件之所以成为前端性能瓶颈,主要有以下几个原因:
mousemove
input
所以,防抖和节流就像是给这些“急性子”的事件处理函数套上了缰绳,让它们在合理的频率下工作,而不是一味地“瞎跑”。
选择防抖还是节流,其实没有绝对的对错,关键在于你的业务场景和用户体验预期。我通常会问自己:这个操作是需要用户“完成”后才触发,还是需要“持续”但有频率限制地触发?
防抖的典型应用场景:
input
resize
节流的典型应用场景:
scroll
mousemove
简单来说,如果你关心的是“结果”,即操作最终完成时的状态,那防抖更合适;如果你关心的是“过程”,即操作过程中需要持续反馈,但又想控制其频率,那节流更合适。
我自己刚开始写防抖节流的时候,最头疼的就是
this
this
this
this
func.apply(context, args)
func.call(context, ...args)
this
this
this
this
// 在上面的实现中,`const context = this;` 已经处理了这个问题。
// 如果使用箭头函数,可以这样写:
// return (...args) => {
// const context = this; // 这里的this是debounce/throttle的调用者,不是事件源
// clearTimeout(timeoutId);
// timeoutId = setTimeout(() => {
// func.apply(context, args);
// }, delay);
// };
// 更常见的做法是让闭包内的this指向包装函数被调用时的this,如示例代码所示。参数传递问题:
event
...args
apply
call
// 示例代码中的 `function(...args)` 和 `func.apply(context, args)` 已经处理了这个问题。
立即执行(leading
trailing
leading
leading
trailing
// 带有立即执行选项的防抖
function debounceWithLeading(func, delay, immediate = false) {
let timeoutId;
let invoked = false; // 标记是否在当前防抖周期内已经执行过
return function(...args) {
const context = this;
const callNow = immediate && !invoked;
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
invoked = false; // 定时器结束后重置标记
if (!immediate) { // 如果不是立即执行模式,就在这里执行
func.apply(context, args);
}
}, delay);
if (callNow) { // 如果是立即执行模式且当前周期未执行过
func.apply(context, args);
invoked = true;
}
};
}取消(cancel
cancel
// 带有取消功能的防抖
function debounceWithCancel(func, delay) {
let timeoutId = null;
let debounced = function(...args) {
const context = this;
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(context, args);
}, delay);
};
debounced.cancel = function() {
clearTimeout(timeoutId);
timeoutId = null;
};
return debounced;
}第三方库:
_.debounce
_.throttle
this
// 使用 Lodash
// import _ from 'lodash';
// const myEfficientFn = _.debounce(() => console.log('我被执行了!'), 500, { leading: true });
// const myEfficientScroll = _.throttle(() => console.log('滚动事件被节流了!'), 200, { trailing: false });理解这些“坑”和优化技巧,能让你在实际开发中写出更健壮、更符合业务需求的防抖和节流函数。
以上就是JS 防抖与节流实现原理 - 控制高频事件回调的执行频率优化的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号