🚀 JS 最常用的性能优化 防抖和节流
先看个问题:
- 你在搜索框打字,每打一个字就发一次请求 → 1 秒打 5 个字,发 5 次请求 → 服务器压力大,页面卡顿
- 你滚动页面,每滚动 1px 就执行一次代码 → 1 秒滚动 100px,执行 100 次 → 浏览器累死,页面掉帧
核心痛点 :有些事件(input、scroll、resize、mousemove)触发频率极高,如果每次触发都执行代码,会严重浪费性能,导致页面卡顿。
- 防抖 :"等你停下来,我再执行" 大部分面向后端请求的
- 节流 :"每隔一段时间,只执行一次" 大部分面向前端渲染的
防抖
事件触发后,等待一段时间 (比如 300ms),如果这段时间内没有再次触发 ,才执行代码;如果又触发了 ,就重新计时。
js
/**
* 防抖函数
* @param {Function} fn - 要执行的函数
* @param {Number} delay - 等待时间(毫秒)
* @returns {Function} - 防抖后的函数
*/
function debounce(fn, delay) {
let timer = null; // 存定时器的变量
// 返回一个新函数
return function(...args) {
// 如果之前有定时器,先清除(重新计时)
if (timer) clearTimeout(timer);
// 重新设置定时器,等待 delay 毫秒后执行 fn
timer = setTimeout(() => {
fn.apply(this, args); // 用 apply 保证 this 指向正确,args 是参数
}, delay);
};
}
真实场景:搜索框实时搜索
html
<input type="text" id="searchInput" placeholder="输入搜索内容...">
<script>
// 模拟搜索请求
function search(keyword) {
console.log('发送搜索请求:', keyword);
}
// 用防抖包装搜索函数:停止输入 300ms 后才发请求
const debouncedSearch = debounce(search, 300);
const input = document.getElementById('searchInput');
input.addEventListener('input', function(e) {
debouncedSearch(e.target.value); // 调用防抖后的函数
});
// 防抖函数
function debounce(fn, delay) {
let timer = null;
return function(...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
</script>
节流
事件持续触发时,每隔固定时间 (比如 200ms),只执行一次代码,不管中间触发了多少次。
js
/**
* 节流函数(时间戳版)
* @param {Function} fn - 要执行的函数
* @param {Number} interval - 间隔时间(毫秒)
* @returns {Function} - 节流后的函数
*/
function throttle(fn, interval) {
let lastTime = 0; // 记录上次执行的时间
return function(...args) {
const nowTime = Date.now(); // 当前时间
// 如果当前时间 - 上次执行时间 >= 间隔时间,才执行
if (nowTime - lastTime >= interval) {
fn.apply(this, args);
lastTime = nowTime; // 更新上次执行时间为当前时间
}
};
}
// 定时器
function throttle(fn, interval) {
let timer = null;
return function(...args) {
// 如果没有定时器,才设置
if (!timer) {
timer = setTimeout(() => {
fn.apply(this, args);
timer = null; // 执行完后清空定时器,允许下次执行
}, interval);
}
};
}
真实场景:滚动页面显示位置
html
<div style="height: 2000px;">向下滚动试试...</div>
<div id="scrollPos" style="position: fixed; top: 10px; left: 10px;"></div>
<script>
// 显示滚动位置
function showPos() {
const pos = window.scrollY;
document.getElementById('scrollPos').innerText = `滚动位置:${pos}px`;
}
// 用节流包装:每隔 200ms 只执行一次
const throttledShowPos = throttle(showPos, 200);
window.addEventListener('scroll', throttledShowPos);
// 节流函数
function throttle(fn, interval) {
let lastTime = 0;
return function(...args) {
const nowTime = Date.now();
if (nowTime - lastTime >= interval) {
fn.apply(this, args);
lastTime = nowTime;
}
};
}
</script>