防抖和节流

425 阅读3分钟

为什么需要防抖和节流

在前端会出现一种情况,就是某些事件的触发会比较频繁,比如:mousemove,scroll,resize等等。这些事件的触发是比较频繁的,那么其对应的事件处理函数就会频繁执行,如果事件处理函数的逻辑简单还好说,要是事件处理函数的逻辑比较复杂,或者说事件处理函数附带了很多前端效果的话,不仅仅会影响性能,还会造成很多意向不到的问题。

那么,为了解决这些问题,也从用户体验的角度出发,有防抖和节流两种方案。

防抖

所谓防抖就是,在固定的时间内,事件处理函数仅执行一次。举个例子,我们通过代码控制,让某个时间处理函数在每5s内仅触发一次,那么即使用户的操作再频繁,那么两次触发的时间最小是 5s,这在一定程度上就可以控制用户的触发频率。

代码如下:

// 假设mouseMove是一个事件处理函数,当鼠标滑动的时候,该函数会频繁的触发
function mouseMove(){ ... }
// 我们现在给函数添加防抖功能

// 封装一个附加防抖功能的函数
// often {Function}触发事件后需要处理的逻辑
// time {Number} 控制最大触发频率的时间
function antiShake(often, time){
	let timer = null; 
	return (e) => { // 该函数会作为事件处理函数,频繁触发
    	// 每次触发的时候先清除之前的延时器,再生成新的延时器
    	timer = setTimeout(often,time, e) // 把事件对象传递给often
    }
	 
}
// 使用
documnet.onmousemove = antiShake(mouseMove, 300)

节流

所谓节流就是,事件处理函数触发一次,等待一定的时间之后,才会被允许再次触发。可以理解为当事件处理函数被触发后,就进入冷却期,等冷却期结束后,才可以再次被触发

代码如下:

// 假设mouseMove是一个事件处理函数,当鼠标滑动的时候,该函数会频繁的触发
function mouseMove(){ ... }

// 现在我们给函数添加节流功能

// 封装一个附加节流功能的函数
function choke(often, time){
	let isEmit = true; // 表示事件处理函数是否可以触发
    return (e) => { // 该函数会作为事件处理函数,频繁触发
    	if(isEmit){
        	isEmit = false; // 开始进入冷却期
            setTimeout(() => (isEmit = true), time) // 开始计时,计时结束后,修改isEmit
            often(e); // 让真正的包裹逻辑的函数开始执行
        }
    }
    
}

// 使用

document.onmousemove = choke(mouseMove, 300)

区别

从代码上看,我们发现这两种方式都是在限制用户的触发频率,但是不同的是:

假设时间处理函数一直在频繁触发的状态下

防抖的方式会让该事件处理函数一直不触发,等频繁触发结束的时候仅触发一次

节流的方式会让该事件处理函数按照我们设定的时间,周期性的触发。