JavaScript 性能优化:防抖与节流函数
网页中频繁触发的事件(如滚动、窗口缩放、输入搜索)会大量消耗浏览器资源,导致页面卡顿甚至崩溃。防抖(debounce)和节流(throttle)是两种经典解决方案,能有效控制函数执行频率,提升性能。
防抖:只在最后一次触发后执行
防抖的核心思想是:在指定时间间隔内,如果某事件被连续触发,则只在最后一次触发后的等待期结束后执行一次回调函数。适用于搜索框输入、窗口 resize 等场景。
实现防抖函数
编写一个通用的防抖函数:
function debounce(func, delay) {
let timer = null;
return function (...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
func是你希望限制执行频率的目标函数。delay是等待时间(单位:毫秒),例如300表示 300 毫秒内无新触发才执行。- 函数返回一个新函数,该函数内部维护一个定时器
timer。 - 每次调用返回的函数时,先清除旧定时器,再设置新定时器。
- 只有当连续两次调用间隔超过
delay时,func才会被真正执行。
使用防抖优化搜索框
假设有一个实时搜索功能,用户每输入一个字符就向服务器请求结果:
<input type="text" id="searchInput" placeholder="请输入关键词">
绑定防抖后的处理函数:
const searchInput = document.getElementById('searchInput');
function handleSearch(query) {
console.log('正在搜索:', query);
// 此处可替换为实际的 API 调用
}
const debouncedSearch = debounce(handleSearch, 500);
searchInput.addEventListener('input', (e) => {
debouncedSearch(e.target.value);
});
现在,用户快速输入“apple”时,handleSearch 不会在每个字母输入时都执行,而是在用户停止输入 500 毫秒后,仅执行一次并传入完整关键词 “apple”。
节流:固定时间间隔内最多执行一次
节流的核心思想是:无论事件触发多么频繁,在指定时间间隔内,回调函数最多只执行一次。适用于滚动加载、按钮点击、鼠标移动等场景。
实现节流函数
编写一个通用的节流函数(基于时间戳方式):
function throttle(func, delay) {
let lastExecTime = 0;
return function (...args) {
const currentTime = Date.now();
if (currentTime - lastExecTime >= delay) {
func.apply(this, args);
lastExecTime = currentTime;
}
};
}
lastExecTime记录上一次函数实际执行的时间。- 每次调用返回的函数时,获取当前时间并与上次执行时间比较。
- 只有间隔大于等于
delay时,才执行func并更新lastExecTime。
注意:此实现保证了首次触发立即执行,但可能在结束时漏掉最后一次触发。若需确保末尾执行,可结合定时器实现“leading + trailing”模式,但基础场景通常无需如此复杂。
使用节流优化滚动事件
监听 scroll 事件常用于实现“滚动到底部加载更多”:
function onScroll() {
const scrollTop = window.scrollY;
const windowHeight = window.innerHeight;
const documentHeight = document.documentElement.scrollHeight;
if (scrollTop + windowHeight >= documentHeight - 100) {
console.log('接近底部,准备加载更多...');
// 此处可触发数据加载
}
}
const throttledScroll = throttle(onScroll, 200);
window.addEventListener('scroll', throttledScroll);
即使用户快速滚动页面,onScroll 也最多每 200 毫秒执行一次,避免高频计算导致主线程阻塞。
防抖 vs 节流:如何选择?
| 场景 | 推荐策略 | 原因 |
|---|---|---|
| 搜索框输入、表单验证 | 防抖 | 用户意图是最终输入结果,中间过程无需响应 |
| 滚动事件、窗口 resize、鼠标移动 | 节流 | 需要持续响应,但必须限制频率以保流畅 |
| 按钮重复点击(如提交订单) | 防抖 或 节流 | 防止多次提交;通常用防抖更直观(点击后一段时间内禁用) |
进阶:带取消功能的防抖
有时需要在特定条件下取消已安排的执行(例如组件卸载时):
function debounceWithCancel(func, delay) {
let timer = null;
const debounced = function (...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, delay);
};
debounced.cancel = function () {
if (timer) {
clearTimeout(timer);
timer = null;
}
};
return debounced;
}
使用时可随时调用 cancel() 方法:
const debouncedSave = debounceWithCancel(saveData, 1000);
// 在适当时候(如离开页面前)
debouncedSave.cancel(); // 确保不会在后台意外保存
验证优化效果
打开浏览器开发者工具(按 F12),切换到 Performance 面板:
- 点击 录制按钮(●)。
- 操作 页面(如快速滚动或输入)。
- 停止 录制。
- 观察 Main 线程中的 Function Call 记录:
- 未优化时,事件回调会密集出现。
- 应用防抖/节流后,回调数量显著减少,帧率(FPS)更稳定。
通过这种方式,你能直观看到性能提升。

暂无评论,快来抢沙发吧!