防抖和节流
在开发中我们经常会遇见一些频繁触发的事件,比如resize、scroll、mousemove等。那如果这些事件是复杂的回调函数或是数据请求,连续的被触发之后,浏览器反应不过来,就会出现卡顿的现象,为了解决这个问题,就出现了两种解决方案:防抖(debounce)和 节流(throttle)
防抖(debounce)
原理:触发高频事件后n秒内,函数只会执行一次,如果在一个事件触发的n秒内再次触发这个事件,那会以新的触发时间重新计算。总之,就是在触发完事件的n秒内不再触发事件,才会再执行函数。
实现:
function debounce(fn, wait) {
var timer = null;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, arguments);
}, wait);
}
}
若想立即执行并在n秒内取消 debounce 函数的做法(immediate参数判断是否是立刻执行)
function debounce(fn, wait, immediate) {
var timer, result;
var debounced = function () {
if (timer) clearTimeout(timer);
if (immediate) {
// 如果已经执行过,不再执行
var callNow = !timeout;
timer = setTimeout(() => {
timer = null;
}, wait);
if (callNow) result = func.apply(this, arguments)
}
else {
timer = setTimeout(() => {
fn.apply(this, arguments);
}, wait);
}
return result;
};
debounced.cancel = function() {
clearTimeout(timer);
timer = null;
};
return debounced;
}
节流(throttle)
原理:持续触发事件,每隔一段时间,只执行一次函数。就是说会稀释函数的执行频率。
实现:关于节流的实现,有两种主流的实现方式,一种是使用时间戳,一种是设置定时器。
使用时间戳:
function throttle(fn, wait) {
var previous = 0;
return function () {
var now = +new Date();
if (now – previous > wait) {
fn.apply(this, arguments);
previous = now;
}
}
}
使用定时器:当事件触发时,设置一个定时器,再次触发事件时,如果定时器存在,就不执行,直到执行定时器,执行函数,清空定时器。
function throttle(fn, wait) {
var timer;
var previous = 0;
return function() {
if (!timer) {
timer = setTimeout(() => {
timer = null;
fn.apply(this, arguments)
}, wait)
}
}
}
比较以上两种方法:
时间戳方法事件会立刻执行,定时器方法事件会在 n 秒后第一次执行;
时间戳方法事件停止触发后没有办法再执行事件,定时器方法事件停止触发后依然会再执行一次事件。