一、前言
想必前端工程师看到这两个单词都是十分的熟悉。这两种技术,都是为了避免短时间内大量触发同一种操作所做的努力。特别是,当这一操作代价比较大,如涉及到DOM操作或浏览器回流等行为时,会造成极大的性能损耗,严重时甚至会导致浏览器的崩溃。
二、一些错误的认识
debounce(防抖)和throttle(节流),这两种方法的原理很类似。都是利用setTimeout和闭包控制事件处理程序的触发频率。然而两种方法的具体实现方法及最终效果又有细微的不同。正因为两种方法是如此的类似,以及网上资料的混乱,加重了初学者的学习成本。
- 浅谈javascript的函数节流 腾讯AlloyTeam团队blog
- javascript高级语言程序设计(第三版)
- 函数节流与函数防抖
function debounce(fn, interval = 300) {
let timeout = null;
return function () {
clearTimeout(timeout);
timeout = setTimeout(() => {
fn.apply(this, arguments);
}, interval);
};
}
复制代码
对于同一段代码,这三处地方却给出了完全不同的解释:AlloyTeam团队和javascript高级语言程序设计都将其标注为throttle,而包括第三篇blog在内的几乎所有其他地方都将其标注为debounce。我陷入了疑惑。本着前两者的权威性,一开始我也认为这是防抖,但是,直到我找到了这个:
其中明确指出“Debouncing enforces that a function not be called again until a certain amount of time has passed without it being called”。因此,上述代码应该是debounce(防抖)。 而相应的,throttle(节流)对应的描述则是“Throttling enforces a maximum number of times a function can be called over time”。
三、debounce(防抖)
废话不多说,先上段代码
var debounce = function(fn, delay){
var timer = null;
return function(){
var context = this, args = arguments;
clearTimeout(timer);
timer = setTimeout(function(){
fn.apply(context, args);
}, delay);
};
};
div.onclick = debounce(() => { console.log('点击事件') }, 1000);
复制代码
debounce的效果是:当事件被触发时,事件处理函数不会立即执行,而是会被放到setTimeout中延时delay执行;而当delay时间内再次触发事件,则前一个setTimeout生成的ID会被clearTimeout清除,同时生成新的ID。这样,只有在delay时间段内,没有新的event,事件处理函数才会真正执行。
这一方法可以用在输入框中,若是每次input事件触发时,都发送一个ajax操作,则会造成极大的性能浪费。使用debounce,则只会在用户停止输入后才会发送ajax请求。
但是这一方式也有自己的问题:如果事件一直源源不断的触发,则可能导致在很长一段时间内,事件处理程序都不会被调用。在某些场景下,这可能会导致用户体验不佳。比如说,需要为dialog弹窗增加鼠标拖动的功能,如果使用debounce的方法,则可能会导致在鼠标移动期间,弹窗一直不动,直到鼠标停止移动,弹窗会一下子跳到终止位置。这显然是不合理的。
四、throttle(节流)
function throttle(fun, delay) {
let last, deferTimer
return function (args) {
let that = this
let _args = arguments
let now = +new Date()
if (last && now < last + delay) {
clearTimeout(deferTimer)
deferTimer = setTimeout(function () {
last = now
fun.apply(that, _args)
}, delay)
}else {
last = now
fun.apply(that,_args)
}
}
}
let throttleAjax = throttle(ajax, 1000)
let inputc = document.getElementById('throttle')
inputc.addEventListener('keyup', function(e) {
throttleAjax(e.target.value)
})
复制代码
throttle的的效果和debounce类似,也是会限制delay时间内事件的触发次数。但是,相较于debounce,throttle的好处在于当事件连续触发时,每delay时间段,事件处理函数都会执行一次。从而避免了debounce的连续触发事件,导致事件处理函数一直无法调用的极端情况。
throttle可以用在滚屏预加载,弹窗拖动等场景。这些场景的特征是,在事件节流的基础上,要求每隔一段时间,至少执行一次事件处理函数。
五、总结
debounce解决了短时间内大量事件触发的问题,只有当delay时间段内,没有新的事件触发,才会真正执行事件处理函数;但是,极端情况下,事件处理函数可能一直得不到执行。
throttle则在解决事件节流的基础上,定期执行事件处理函数。
ps:以上纯属个人理解,如有错误,欢迎大家指出