以下场景往往由于事件频繁被触发,因而频繁执行DOM操作、资源加载等重行为,导致UI停顿甚至浏览器崩溃。
window对象的resize debounce
scroll事件 throttle
拖拽时的mousemove事件 debounce
射击游戏中的mousedown、keydown事件 debounce
文字输入、自动完成的keyup事件 debounce
实际上对于window的resize事件,实际需求大多为停止改变大小n毫秒后执行后续处理;而其他事件大多的需求是以一定的频率执行后续处理。针对这两种需求就出现了debounce和throttle两种解决办法。
debounce
当频繁改变窗口大小时,你会发现 fn压根没有被调用过!直到你停下来后,fn才会被调用一次。
const debounce = (fn, ms = 0) => {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn.apply(this, args), ms);
};
};
// 使用
window.addEventListener(
'resize',
debounce(() => {
console.log(window.innerWidth);
}, 1000)
);
throttle
当频繁改变窗口大小时,throttle 会保证在每 wait时间调用 fn一次。
(我们的直观感觉是使用 throttle 方法相比于 debounce 方法事件触发的频率更高一些)
const throttle = (fn, wait) => {
let inThrottle, lastFn, lastTime;
return function() {
const context = this,
args = arguments;
if (!inThrottle) {
fn.apply(context, args);
lastTime = Date.now();
inThrottle = true;
} else {
clearTimeout(lastFn);
lastFn = setTimeout(function() {
if (Date.now() - lastTime >= wait) {
fn.apply(context, args);
lastTime = Date.now();
}
}, wait - (Date.now() - lastTime));
}
};
};
参考链接:
脚本之家:debounce与throttle的简单版
博客:debounce与throttle的深入版
掘金: 函数节流与函数防抖
debounce 与throttle 的适用场景
奇舞周刊的总结