函数节流 (Throttle)
节流是指在指定时间间隔内,函数最多只执行一次。无论事件触发多少次,在时间间隔内只执行一次。
时间戳实现
function throttle(fn, delay = 300) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
const context = this;
// 如果距离上次执行超过 delay,则执行
if (now - lastTime >= delay) {
fn.apply(context, args);
lastTime = now;
}
};
}
// 使用
const handleScroll = throttle(() => {
console.log('滚动');
}, 200);
window.addEventListener('scroll', handleScroll);
定时器实现
function throttle(fn, delay = 300) {
let timer = null;
return function(...args) {
const context = this;
if (!timer) {
timer = setTimeout(() => {
fn.apply(context, args);
timer = null;
}, delay);
}
};
}
// 使用
const handleResize = throttle(() => {
console.log('窗口大小改变');
}, 300);
window.addEventListener('resize', handleResize);
时间戳 + 定时器(首尾都执行)
function throttle(fn, delay = 300) {
let lastTime = 0;
let timer = null;
return function(...args) {
const now = Date.now();
const context = this;
const remaining = delay - (now - lastTime);
if (remaining <= 0 || remaining > delay) {
// 立即执行
if (timer) {
clearTimeout(timer);
timer = null;
}
lastTime = now;
fn.apply(context, args);
} else if (!timer) {
// 延迟执行
timer = setTimeout(() => {
lastTime = Date.now();
timer = null;
fn.apply(context, args);
}, remaining);
}
};
}
取消节流函数
function throttle(fn, delay = 300) {
let timer = null;
let lastTime = 0;
const throttled = function(...args) {
const now = Date.now();
const context = this;
if (now - lastTime >= delay) {
fn.apply(context, args);
lastTime = now;
} else if (!timer) {
timer = setTimeout(() => {
lastTime = Date.now();
timer = null;
fn.apply(context, args);
}, delay);
}
};
// 添加取消方法
throttled.cancel = function() {
if (timer) {
clearTimeout(timer);
timer = null;
}
};
return throttled;
}
// 使用
const handleScroll = throttle(() => {
console.log('滚动');
}, 200);
// 取消
handleScroll.cancel();
实际应用场景
滚动事件
// 滚动加载
const loadMore = throttle(async () => {
const data = await fetchData();
appendData(data);
}, 300);
window.addEventListener('scroll', () => {
const scrollTop = window.pageYOffset;
const windowHeight = window.innerHeight;
const documentHeight = document.documentElement.scrollHeight;
// 距离底部 100px 时加载
if (scrollTop + windowHeight >= documentHeight - 100) {
loadMore();
}
});
鼠标移动
// 鼠标移动轨迹
const trackMouse = throttle((e) => {
console.log('鼠标位置:', e.clientX, e.clientY);
}, 100);
window.addEventListener('mousemove', trackMouse);
按钮点击
// 防止重复提交
const submitForm = throttle(async (data) => {
await api.submit(data);
showSuccess('提交成功');
}, 1000);
button.addEventListener('click', () => {
submitForm(formData);
});
Canvas 动画
// 优化动画渲染
const render = throttle(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
draw();
requestAnimationFrame(render);
}, 16); // 约 60fps
render();
防抖 vs 节流
| 特性 | 防抖 (Debounce) | 节流 (Throttle) |
|---|---|---|
| 执行时机 | 事件停止后 | 固定时间间隔 |
| 适用场景 | 搜索、验证、保存 | 滚动、拖拽、点击 |
| 执行次数 | 可能 0 次或 1 次 | 固定频率执行 |
性能对比
// 高频事件(如滚动)
// 没有节流:每秒可能触发 60 次(60fps)
// 有节流:每秒最多触发 3-5 次(200ms 间隔)
// 减少计算量
// 滚动 1000px
// 没有节流:约 60 次处理
// 有节流:约 5 次处理