JavaScript 防抖与节流实战:原理、实现与优化
本文深入解析 JavaScript 中防抖(debounce)与节流(throttle)的核心原理及代码实现。通过对比时间戳与定时器两种节流方案,详细阐述如何结合两者优势,实现首次立即执行、间隔固定执行且最后一次也能触发的完整节流逻辑,有效解决高频事件导致的页面卡顿问题。
防抖与节流概述
在前端日常开发中,滚动等高频事件若频繁触发复杂计算的回调函数,极易引发页面卡顿。为优化性能,通常需要将多次调用合并为单次操作,JS 中这种优化手段被称为防抖(debounce)和节流(throttle)。
函数防抖实现
函数防抖
防抖机制要求:在持续触发事件的过程中,必须等待一段设定的时间且期间不再次触发,处理函数才会执行。若在延时结束前再次触发,则重新计时。换言之,若用户连续触发且间隔小于设定时间,防抖模式下最终仅执行最后一次触发。
function debounce(fn, wait) {
var timeout = null; //定义一个定时器
return function() {
if(timeout !== null)
clearTimeout(timeout); //清除这个定时器
timeout = setTimeout(fn, wait);
}
}
// 处理函数
function handle() {
console.log(Math.random());
}
// 滚动事件
window.addEventListener('scroll', debounce(handle, 1000));

如代码所示,持续触发 scroll 事件时,handle 函数仅在 1 秒内执行一次。滚动过程中未持续执行,从而有效降低了性能损耗。
函数节流原理
函数节流
节流机制要求:在持续触发事件时,保证在一定时间周期内只调用一次处理函数。即使用户连续触发且间隔小于设定值,节流模式也会每隔固定时间执行一次。
一句话总结防抖与节流的区别:防抖将多次执行合并为最后一次执行,而节流将多次执行转化为每隔一段时间执行一次。实现函数节流主要采用时间戳和定时器两种方法。
时间戳实现方案
例如
var throttle = function(func, delay) {
var prev = Date.now();
return function() {
var context = this; //this指向window
var args = arguments;
var now = Date.now();
if (now - prev >= delay) {
func.apply(context, args);
prev = Date.now();
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
上述节流函数利用时间戳机制,使第一次滚动事件立即执行回调,此后每隔 1000ms 执行一次;在不足 1000ms 的时间窗口内,滚动事件不会触发执行。
var throttle = function(func, delay) {
var timer = null;
return function() {
var context = this;
var args = arguments;
if (!timer) {
timer = setTimeout(function() {
func.apply(context, args);
timer = null;
}, delay);
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
定时器实现方案
事件触发时设置定时器,若未到1000ms则定时器为null,到达规定时间执行函数并清除定时器。首次触发事件后,需等待1000ms才执行函数,产生延迟效果,且此期间滚动事件不执行函数。 仅使用时间戳或定时器均有缺陷,期望首次触发立即执行,最后一次触发也能执行一次事件处理函数。
var throttle = function(func, delay) {
var timer = null;
var startTime = Date.now(); //设置开始时间
return function() {
var curTime = Date.now();
var remaining = delay - (curTime - startTime); //剩余时间
var context = this;
var args = arguments;
clearTimeout(timer);
if (remaining <= 0) { // 第一次触发立即执行
func.apply(context, args);
startTime = Date.now();
} else {
timer = setTimeout(func, remaining); //取消当前计数器并计算新的remaining
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));

综合优化方案
单独使用时间戳或定时器均存在缺陷:时间戳方案首次执行但最后一次可能不执行,定时器方案最后一次执行但首次有延迟。理想状态是首次触发立即执行,且最后一次触发也能执行处理函数。通过引入开始时间 startTime、当前时间 curTime 和剩余时间 remaining,当 remaining 小于等于 0 时执行处理函数。这样既保证首次立即执行,又确保每隔 delay 时间执行一次;若未到时间,则在 remaining 后触发,保证最后一次也能执行。若在 remaining 时间内再次触发,则取消当前计数器并重新计算 remaining 时间。结合时间戳与定时器,实现了首次立即执行、间隔固定执行及最后一次执行的完整效果,可灵活应用于开发中。
注:防抖和节流能有效减少浏览器引擎损耗,防止页面堵塞卡顿,开发者应熟练掌握。感谢原作者的技术分享。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
为什么JS调用reload后页面没有立即更新
2026-11-05 13:38:29
crypto-js 加密结果与 Java/Python 后端不一致怎么解决
2026-11-05 12:09:48
js文件缓存导致代码不更新怎么强制刷新
2026-11-04 22:30:24
JS逆向调试技巧:编写Hook脚本监控加密函数输入输出
2026-11-04 10:42:46
Babylon.js 性能优化技巧:实例化网格与遮挡剔除
2026-11-03 23:43:22
