跳到主要内容

函数节流 (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 次处理