防抖(debounce)和节流(throttle)实现及原理讲解
防抖和节流是前端开发中常用的两种性能优化技术,主要用于控制事件触发的频率。下面我将分别介绍它们的实现和原理。
防抖(debounce)
原理
防抖的核心思想是:在事件被触发后,等待一段时间再执行回调函数。如果在这段时间内事件又被触发,则重新计时。这样可以确保只有在事件停止触发一段时间后才会执行回调。
适用场景
搜索框输入联想(等待用户停止输入后再发送请求)
窗口大小调整(等待调整结束后再计算布局)
表单验证(等待用户停止输入后再验证)
实现代码
function debounce(fn, delay) {let timer = null;return function(...args) {// 每次触发时清除之前的定时器clearTimeout(timer);// 设置新的定时器timer = setTimeout(() => {fn.apply(this, args);}, delay);};
}
使用示例
// 模拟一个搜索函数
function search(query) {console.log(`搜索: ${query}`);
}// 创建防抖版本的搜索函数
const debouncedSearch = debounce(search, 500);// 模拟连续输入
debouncedSearch('a');
debouncedSearch('ab');
debouncedSearch('abc');
// 只有最后一次调用会在500ms后执行
节流(throttle)
原理
节流的核心思想是:在一定时间间隔内,只执行一次回调函数。无论这段时间内事件触发多少次,都只执行一次。
适用场景
滚动事件监听(每隔一段时间检查位置)
鼠标移动事件(限制处理频率)
按钮点击防重复提交
实现代码
时间戳版本
function throttle(fn, delay) {let lastTime = 0;return function(...args) {const now = Date.now();// 如果距离上次执行时间超过了delay,则执行if (now - lastTime >= delay) {fn.apply(this, args);lastTime = now;}};
}
定时器版本
function throttle(fn, delay) {let timer = null;return function(...args) {if (!timer) {timer = setTimeout(() => {fn.apply(this, args);timer = null;}, delay);}};
}
结合版本(更精确)
function throttle(fn, delay) {let timer = null;let lastTime = 0;return function(...args) {const now = Date.now();const remaining = delay - (now - lastTime);clearTimeout(timer);if (remaining <= 0) {fn.apply(this, args);lastTime = now;} else {timer = setTimeout(() => {fn.apply(this, args);lastTime = Date.now();}, remaining);}};
}
使用示例
// 模拟一个处理滚动函数
function handleScroll() {console.log('处理滚动事件');
}// 创建节流版本的处理函数
const throttledScroll = throttle(handleScroll, 200);// 模拟频繁滚动事件
window.addEventListener('scroll', throttledScroll);
// 无论滚动多快,最多每200ms执行一次
防抖与节流的区别
特性 | 防抖(debounce) | 节流(throttle) |
---|---|---|
触发频率 | 只在事件停止触发后执行 | 固定时间间隔执行 |
执行次数 | 多次触发只执行一次 | 多次触发按固定频率执行 |
适用场景 | 搜索联想、窗口resize | 滚动事件、鼠标移动 |
效果 | 等待用户完成操作 | 稀释操作频率 |
实际应用技巧
参数选择:
防抖的延迟时间通常设置为300-500ms
节流的间隔时间根据场景不同,滚动事件常用16ms(60fps)或100ms
立即执行版本:
有时候我们需要防抖函数在第一次触发时立即执行,然后才开始防抖:function debounceImmediate(fn, delay, immediate = true) {let timer = null;return function(...args) {const callNow = immediate && !timer;clearTimeout(timer);timer = setTimeout(() => {if (!immediate) {fn.apply(this, args);}timer = null;}, delay);if (callNow) {fn.apply(this, args);}}; }
取消功能:
可以为防抖和节流函数添加取消功能:function debounceWithCancel(fn, delay) {let timer = null;const debounced = function(...args) {clearTimeout(timer);timer = setTimeout(() => {fn.apply(this, args);}, delay);};debounced.cancel = function() {clearTimeout(timer);timer = null;};return debounced; }
防抖和节流是前端性能优化的重要技术,合理使用可以显著提升页面性能和用户体验。