一个使用场景:
某些浏览器事件可能会在短时间内高频触发,比如:整窗口大小或滚动页面。
如果给窗口滚动事件添加一个事件监听器,然后用户不停地快速滚动页面,那你的事件可能在短短数秒之内被触发数千次。这会导致非常严重的性能问题。
所以如果功能涉及滚动事件,窗口调整事件,或者键盘事件鼠标事件等,你可能需要警觉起来,是否有必要使用 debouncing 或者 throttling 来提高页面速度与性能。
Debouncing(防抖动)
概念
debouncing(防抖动)是解决上述问题的一个方案,它的做法是 限制下次函数调用之前必须等待的时间间隔,也就是说:强制一个函数在某个连续时间段内只执行一次,哪怕它本来会被调用多次。正确实现 debouncing 的方法是:将若干个函数调用合并为一次,只有在空闲时间大于或等于给定值的时候,才执行调用方法。
实现
简单的实现一个 debounce
方法,接收两个参数,一个是需要防抖动的函数 fn
,另一个是延迟时间delay
funciton debouncing(fn, delay) {
let timer; //定时器
return function() {
// 保存函数调用时的上下文和参数,传递给 fn
const context = this;
const args = arguments;
clearTimeout(timer);
timer = setTimeout(function(){
fn.apply(context, args);
}, delay);
}
}
该 debounce
的使用方法如下:
$(document).on('keyup', debounce(function(e) {
// 代码
}, 250))
Throttling(节流阀)
概念
throttling(节流阀)则是另一种解决问题的方案,它的做法是固定一段时间内函数调用的频率,它与 debouncing 最大的不同之处在于,throttling 会保证至少调用一次。
实现
与 debounce
类似,接收两个参数,一个是需要截流的函数 fn
, 另一个是函数执行间隔阈值 threshhold
。
function throttle(fn, threshhold) {
let timer; //定时器
let last; //记录上次时间
threshhold || (threshhold = 250); //默认间隔为250ms
return function() {
// 保存函数调用时的上下文和参数,传递给 fn
var context = this;
var args = arguments;
let now = +new Date();
// 如果上次调用距本次调用的时间间隔不够,则不执行 fn,并重新计时
if(last && now < last + threshhold){
clearTimeout(timer);
// 保证在当前时间区间结束后,再执行一次 fn
timer = setTimeout({
last = now;
fn.apply(context, args);
}, threshhold);
} else { //如果时间间隔够了,则立刻执行 fn
last = now;
fn.apply(context, args);
}
}
throttle
使用方法如下:
$(document).on('mouvemove', throttle(function(e) {
// 代码
}, 250))
总结
debouncing 和 throttling 的区别还是很明显的:前者把一段时间的多次调用合并成一次,后者把一段时间的多次调用减少为数次。下图的展示十分便于理解。