节流和防抖
防抖
防抖的原理就是:你尽管触发事件,但是我一定在事件触发 n 秒后才执行,如果你在一个事件触发的 n 秒内又触发了这个事件,那我就以新的事件的时间为准,n 秒后才执行,总之,就是要等你触发完事件 n 秒内不再触发事件,我才执行。
/**
* debounce
* @param {*} fn 要防抖执行的函数
* @param {*} wait 延迟执行毫秒数
* @param {*} immediate true 表立即执行,false 表非立即执行
*/
function debounce(fn, wait, immediate) {
let timer, result;
let debounced = function () {
// 将 this 指向正确的对象
let context = this;
// 参数
let args = [...arguments];
if (timer) {
clearTimeout(timer);
}
if (immediate) {
// 如果immediate设置为true, 每次调debounce都会走这个分支
// 第一次执行debounce 或者 wait 时间之后,timer为undifined或null,callNow为true,所以此时会执行一次fn
// wait时间内再次调用debounce,timer有值所以callNow为false,fn不会执行,直到wait时间之后 timer被改为null
let callNow = !timer;
timer = setTimeout(() => {
timer = null;
}, wait);
if (callNow) {
result = fn.apply(context, args);
}
} else {
timer = setTimeout(() => {
fn.apply(context, args);
}, wait);
}
// fn的返回值, immediate参数为true的情况下,result才会取到结果
return result;
};
// 取消防抖
debounced.cancel = function () {
clearTimeout(timer);
timer = null;
};
return debounced;
}
节流
节流的原理很简单,如果你持续触发事件,每隔一段时间,只执行一次事件。节流会稀释函数的执行频率
使用时间戳
function throttle(fn, wait) {
let ctx, args;
let previous = 0;
return function () {
let now = Date.now();
ctx = this;
args = arguments;
if (now - previous > wait) {
fn.apply(ctx, args);
previous = now;
}
};
}
使用定时器
function throttle(fn, wait) {
let timer, ctx;
return function () {
ctx = this;
args = arguments;
if (!timer) {
timer = setTimeout(function () {
timer = null;
fn.apply(ctx, args);
}, wait);
}
};
}
underscore 版
function throttle(func, wait, options) {
let timer, context, args, result;
let previous = 0;
if (!options) options = {};
let later = function () {
previous = options.leading === false ? 0 : Date.now();
timer = null;
func.apply(context, args);
if (!timer) context = args = null;
};
let throttled = function () {
let now = Date.now();
if (!previous && options.leading === false) previous = now;
let remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timer) {
clearTimeout(timer);
timer = null;
}
previous = now;
func.apply(context, args);
if (!timer) context = args = null;
} else if (!timer && options.trailing !== false) {
timer = setTimeout(later, remaining);
}
};
throttled.cancel = function () {
clearTimeout(timer);
previous = 0;
timer = null;
};
return throttled;
}
防抖和节流的区别
防抖是虽然事件持续触发,但只有等事件停止触发后 n 秒才执行函数,节流是持续触发的时候,每 n 秒执行一次函数
函数防抖关注一定时间连续触发的事件只在最后执行一次,而函数节流侧重于一段时间内只执行一次。