在Web开发中,尤其是涉及用户交互的场景,比如窗口滚动、鼠标移动或键盘输入,事件可能会被频繁触发。如果不加以控制,这些高频率的事件触发会导致性能瓶颈,甚至使应用变得不响应。为了应对这种情况,我们通常会采用函数防抖(debounce)和函数节流(throttle)的技术,以优化事件处理的效率和响应性。
函数防抖(Debounce)
概念: 函数防抖是指在一系列高频事件触发后,只在最后一次事件触发后的指定延迟时间后执行一次函数。如果在延迟时间内又有新的事件触发,则会重置延迟计时,直到最后没有新的事件触发为止,才会执行一次函数。
- 比如我们短时间内多次缩放页面,那么我们不应该每次缩放都去执行操作,应该只做一次就好。再比如说监听输入框的输入,不应该每次都去触发监听,应该是用户完成一段输入后在进行触发。
实现方式: 通过闭包和定时器实现。每当事件触发时,检查是否存在一个未执行的定时器,如果有,则清除该定时器并重新设置一个新的定时器。这样可以确保无论事件触发多少次,只要在延迟时间结束后没有新的事件触发,就只会执行一次函数。
应用场景:
- 用户输入搜索框时,每敲击一个字符就发起网络请求,使用防抖可以减少不必要的网络请求。
- 窗口大小变化时,调整布局,使用防抖可以避免因用户拖动边框时频繁调整布局而导致的性能问题。
函数节流(Throttle)
概念: 函数节流是在固定的时间间隔内只执行一次函数。即使事件被连续触发,函数也不会立即执行,而是在上一次执行后的固定时间间隔后才能再次执行。
实现方式: 通过闭包和标记变量实现。每次事件触发时,先检查标记变量,如果标记变量表示可以执行函数,则执行函数并将标记变量设置为不可执行状态,同时启动一个定时器,在设定的时间间隔后将标记变量重置为可执行状态。
应用场景:
- 页面滚动时加载新内容,使用节流可以确保在用户停止滚动后的固定时间间隔内加载新内容,避免频繁加载。
- 鼠标移动时更新光标位置,使用节流可以平滑光标移动,避免过度消耗资源。节省流量。
- 如果一直点,那么请求就会一直发布出去。这里正确的思路应该是第一次点击就发送,然后上一个请求回来后,才能再发。
区别
-
函数防抖防抖是触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间。适用于可以多次触发但触发只生效最后一次的场景。保证在事件停止触发后的一段时间内只执行一次函数,适用于需要在用户完成一系列操作后才进行处理的场景。
-
函数节流节流是高频事件触发,但在n秒内只会执行一次,如果n秒内触发多次函数,只有一次生效,节流会稀释函数的执行频率。保证在固定的时间间隔内只执行一次函数,适用于需要在用户操作过程中定期执行某些操作的场景。
示例代码
函数防抖(Debounce)
function debounce(func, wait) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(this, args);
}, wait);
};
}
const handle = debounce(function() {
console.log('防抖:', Math.random());
}, 500);
window.addEventListener('scroll', handle);
函数节流(Throttle)
function throttle(func, limit) {
let inThrottle;
return function(...args) {
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
const sayHi = throttle(function(e) {
console.log('节流:', e.target.innerWidth, e.target.innerHeight);
}, 500);
window.addEventListener('resize', sayHi);
总结
函数防抖和函数节流都是优化事件处理的有效策略,选择哪一种取决于具体的场景需求。防抖更适合那些需要在用户操作完全结束后才进行处理的场景,而节流则适用于需要在用户操作过程中定期执行某些操作的场景。