JavaScript的防抖和节流
在引入防抖和节流之前,给出一个需求,需要我们监视浏览器的滚动事件,返回滚动条与顶部的距离,我们很容易能写出这样的代码:
1 | function showTop(){ |
但是这样真的可行不?答案是可行但不太合适,因为这个函数的运行频率实在是太高了,只要滚动会一直触发,把浏览器的性能浪费在这里肯定是不合适的。
由此我们引出防抖和节流这两个概念。
防抖
定义:
对于短时间内连续触发的事件,防抖的含义就是当持续触发事件时,一定时间段内没有再次触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。
思路:
在第一次触发事件后,不要立即执行函数,而是给出一个期限值。
- 假如期限值内没有再次触发事件就执行处理函数
- 假如期限值内再次触发了该事件,则重新开始计时。
效果:
短时间内大量触发同一时间,那么只会执行一次处理函数
实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24// fn: 需要防抖的函数
// delay: 期限值,ms
function debounce(fn, delay){
// 借助闭包避免变量全局污染
let timer = null
// 这个才是onscroll绑定的函数
return function(){
// 如果已经在计时中,则取消计时并重新开始计时
if(timer){
clearTimeout(timer)
timer = setTimeout(fn, delay)
// 还未开始计时,则开始计时
}else{
timer = setTimeout(fn, delay)
}
}
}
// 需要防抖的函数
function showTop(){
let scrollTop = document.body.scrollTop || document.documentElement.scrollTop
console.log('滚动条距离顶部的距离:' + scroolTop)
}
// 绑定事件
window.onscroll = debounce(showTop, 1000)上述防抖函数的内部if条件语句是可以优化一下的:
1
2
3
4
5
6
7
8return function(){
// 不管当前是否开始计时,只要触发了都是要开始计时的
// 只是如果当前已经开始,则需要先停掉正在进行的计时器
if(timer){
clearTimeout(timer)
}
timer = setTimeout(fn, delay)
}看起来,好像已经实现需求了,也不会一直触发,避免了性能的过渡损耗
但是此时又出现了一个新的问题,假如一直滑动滚轮让浏览器滚动,岂不是函数永远不会触发了?
节流
当持续触发事件时,保证一定时间段内只调用一次事件处理函数。
简单说就是,如果短时间内大量触发同一事件,那么在函数执行一次后,该函数在指定的事件期限内不会再执行,直至过了这段时间才生效。
直接看代码吧,更加容易理解。
1 | function throttle(fn, delay){ |
当然我们也可以通过时间戳来实现节流操作
1 | function throttle(fn, delay){ |
JavaScript的防抖和节流