防抖和节流,是常用的用于性能优化的手段,降低回调函数的执行频率,节省计算资源,防止页面出现阻塞卡顿现象。
防抖
简单理解防抖的概念,当一个动作频繁被触发,但是只执行一次。
其实从名字上去理解,它就是 “防止手抖” !
实现的核心就是:方法被触发时设定一个时间去判断,如果在等待时间又被触发了,就重新开始计时,一直到事件结束才去触发执行动作。
代码实现
function debounce(fn, delay = 50) {
let timer = null;
return (...args) => {
if(timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.call(this, ...args);
}, delay);
}
}
上面这个实现,是利用 setTimeout 延迟执行方法,并将其赋值给 timer 变量保存,如果是再次被触发则清楚上次的延时,重新开始计算时间。
这种实现最终是只执行多次触发后的最后一次,所以第一次不会实际执行,如果有需求是要立刻执行的,就需要改造一下:
function debounce(fn, delay = 50, immediate = true) {
let timer = null;
return (...args) => {
if (timer) clearTimeout(timer);
immediate && !timer && fn.call(this, ...args);
timer = setTimeout(() => {
timer = null;
!immediate && fn.call(this, ...args);
}, delay);
}
}
最终版通过参数 immediate 判断设么时候执行,当它为 true 的时候,第一次执行 timer 肯定是 false ,所以 fn 会立即执行,然后通过 setTimeout 延时将变量 timer 赋值成 null ,如果在延时时间内再次点击时因为 timer 不为 null 所以不会执行函数。
使用场景
- 按钮点击,避免用户点击过快,导致多次后向后端发送请求。
- 浏览器窗口调整时,使用防抖防止 resize 多次计算
- 获取输入框值得时候,使用防抖在用户输入完后才去获取最终的值
节流
节流就是在一定时间间隔中指触发一次事件,打个比方就像是游戏中的技能的冷却,当技能还在冷却时间的时候键盘摁爆也不会释放技能,只有冷却时间结束了才能使用,然后使用完又开始冷却等待。
代码实现
节流函数可以通过时间戳和定时器来实现
- 时间戳
function throttle(fn, delay) {
let prev = 0;
return (...args) => {
const now = new Date();
if(now - prev > delay) {
fn.call(this, ...args);
prev = now;
}
}
}
- 定时器
function throttle(fn, delay) {
let timer = null;
return (...args) => {
if(!timer) {
timer = setTimeout(() => {
clearTimeout(timer);
timer = null;
fn.call(this, ...args);
}, delay);
}
}
}
使用场景
- 监听滚动事件,隔一段时间再次去处理
- input 框实时搜索请求显示下拉列表情况