1.概念
防抖(Debounce):触发高频事件后 n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触
发,则重新计算时间.节流(Throttle) : 高频事件触发,但在 n 秒内只执行一次,所以节流会稀释函数的执行频率.
). 那么既然是闭包,也就意味着返回值是一个函数,同时这个函数能够将外部函数的变量保存下来.
对于概念,如果有彦祖或者亦非觉得抽象不好记,那我这里有一个巧妙地记忆方式,让我们重新来审视一遍概念:
防抖(Debounce):触发高频事件后 n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触 发,则重新计算时间.
(代入生活中,在和你的同伴一起乘坐电梯下楼时,他突然想起了忘拿法拉利钥匙,于是折返回去,让你摁着电梯门.此时,假如电梯开门后10秒会自动关门,那么在这10秒内,如果你摁了一次开门,这个关门时间就会重新进行计算,直至你不摁为止.这个例子中的高频事件就是电梯的自动关门,只有你最后一次不去摁开门键,电梯才会倒计时去自动关门.)
节流(Throttle):高频事件触发,但在 n 秒内只执行一次.
(代入生活中,你可以理解成:游戏技能的冷却时间,在某段段时间内,你的每个技能只能使用一次)
是不是好理解了一点~
2.代码讲解(如何实现防抖,节流)
(有基础了就继续往下看,基础薄弱就先按我举得例子理解一下概念再进行阅读)
防抖(Debounce)
目标:确保一个函数在事件停止触发一定时间后才执行。
function debounce(func, wait) { //如果你非要问为什么是debounce,那我建议你搜一下这个单词
let timeout; // 声明一个定时器变量,用于存储setTimeout的返回值
// 返回一个函数,这个函数会在每次事件触发时被调用
return function(...args) {
const context = this; // 保存调用时的上下文(this值)
// 清除之前的定时器(如果存在的话),确保不会执行到上一次未执行的函数
clearTimeout(timeout);
// 设置一个新的定时器,在wait毫秒后执行func函数
timeout = setTimeout(() => {
func.apply(context, args); // 使用apply来确保func函数在正确的上下文中执行,并传递所有接收到的参数
}, wait);
};
}
// 使用示例
// 假设有一个需要防抖的函数,比如console.log
const debouncedLog = debounce(console.log, 2000);
// 当你快速连续调用debouncedLog时,实际上只有在最后一次调用后的2秒后,console.log才会执行
debouncedLog('Hello'); // 触发,但设置了2秒后的定时器
debouncedLog('World'); // 再次触发,但之前的定时器被清除,并重新设置了2秒后的定时器
// 等待2秒后,"World"会被打印到控制台而"Hello"不会被打印,因为那是最后一次调用后设置的定时器
节流(Throttle)
目标:确保一个函数在一定时间内只执行一次。
代码分析
function throttle(func, limit) {
let lastFunc;
let lastRan;
return function(...args) {
const context = this;
const now = Date.now();
// 如果这是第一次调用,或者距离上次调用已经超过了limit时间,则执行函数
if (!lastRan || (now - lastRan >= limit)) {
if (lastFunc) clearTimeout(lastFunc); // 清除之前可能设置的定时器(虽然在这个简单实现中通常不需要,但为了完整性保留)
func.apply(context, args);
lastRan = now; // 更新上次执行的时间
}
// 注意:这里没有设置定时器来“等待”到时间间隔结束再执行。
// 节流的核心是确保在指定时间间隔内不重复执行函数,而不是延迟执行。
};
}
// 使用示例
const throttledLog = throttle(console.log, 1000);
// 假设快速连续调用throttledLog
throttledLog('Hello'); // 假设这是第一次调用,会立即执行
throttledLog('World'); // 如果这在1秒内发生,则不会执行
throttledLog('Again'); // 这会再次执行,因为距离上次执行已经超过了1秒
在以上代码中,我们会发现在控制台打印的结果是
//Hello
//Again
这是因为我们快速对throttle进行调用,首先会触发第一次的"Hello"打印,而后因为是快速触发,时间小于1秒,所以不会打印"World",而是打印Again,我们只需要将最后一次触发设置一个时间,让其1秒后再触发,就能正确打印顺序
setTimeout(() => {
throttledLog('Again');
}, 1000);
3.应用场景
防抖(Debounce)的应用场景
防抖技术主要用于事件处理函数中,以确保事件处理函数在最后一次事件触发后的一段时间内才执行。它常用于以下场景:
-
搜索框搜索自动完成:在用户停止输入一定时间后,才进行搜索查询,避免因为用户输入过程中的每个字符都触发搜索请求而导致的性能问题
-
窗口大小调整(resize):在窗口大小调整完成后才执行相关操作,如重新计算布局或重新渲染组件,避免在调整过程中频繁触发
-
表单验证:在用户停止输入后才进行表单验证,提高用户体验,避免在用户输入过程中频繁出现验证提示
-
按钮点击事件:在快速连续点击按钮时,只执行最后一次点击的操作,常用于防止多次提交表单或重复发送请求
节流(Throttle)的应用场景
节流技术用于限制函数在一定时间内的执行频率,确保函数以固定的时间间隔执行。它适用于以下场景:
-
滚动事件(scroll):在滚动过程中,限制处理函数的执行频率,如监听滚动位置来加载内容或更新页面状态,避免因为滚动过快而导致的性能问题
-
游戏循环:在游戏开发中,限制游戏循环的更新频率,确保游戏以稳定的帧率运行
-
高频事件处理:如鼠标移动、触摸滑动等,限制处理函数的执行次数,提高应用的性能和响应能力
-
动画或渲染控制:在动画或复杂UI渲染过程中,限制渲染函数的执行频率,避免因为过度渲染而导致的性能问题
总结
防抖和节流的主要区别在于它们的触发时机和目的。防抖是在最后一次事件触发后的一段时间内才执行函数,而节流是确保函数以固定的时间间隔执行