在某些高频度触发的事件中绑定操作需要用到节流函数。
函数防抖
最常见的打个栗子,如果要在window的滚动事件中发送ajax请求加载更多数据。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body{
height: 1200px;
background-color: lightblue;
}
</style>
</head>
<body>
</body>
<script >
window.addEventListener("scroll",function(){
// 这里发送ajax请求,这里用console.log(1)来代替一下
console.log(1)
})
</script>
</html>
有需要的童鞋可以把此段复制到html文件中自行查看效果。
我们来看看此时页面的表现效果。
此时我们可以看到console.log(1)被高频度触发。如果此时scroll里面触发的是ajax请求,那么在页面滚动的一小段时间内,就会发送45次ajax请求。性能开销不容小觑。
对此问题,封装节流函数如下
function throttle(fn,time){
return function(){
clearTimeout(fn.tId)
fn.tId = setTimeout(fn,time)
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body{
height: 1200px;
background-color: lightblue;
}
</style>
</head>
<body>
</body>
<script >
function throttle(fn,time){
return function(){
clearTimeout(fn.tId)
fn.tId = setTimeout(fn,time)
}
}
window.addEventListener("scroll",throttle(function (){console.log(1)},1000))
</script>
</html>
此时再来看看页面表现
防抖函数的实现原理,throttle函数第一个参数为要执行的函数,第二个参数为间隔时间。那么如何理解这个间隔时间呢。
假设我们在滚动时触发了100次滚动事件,那么就执行了100次throttle函数,第一次执行throttle,清除定时器。然后设置定时器。此时的console.log(1)会在1s之后执行,但是此时执行了第二次throttle函数,它又清除了第一个定时器。生成了第二个定时器。。。。。。,到最后执行第一百次throttle函数,他清除了第99个定时器,设置第一百个定时器。此时throttle全部执行完成,console.log(1)会在1s之后执行。
那么可以这样理解。throttle的第一个参数为待执行函数,第二个参数为停止滚动后几秒内执行函数。上述栗子中的throttle(function(){console.log(1)},1000)表示停止滚动后1s内执行console.log(1)。同时表示着,如果你停止滚动后,在1s内再次滚动,待执行函数也不会执行。它严格执行条件为停止滚动,1s内。
函数节流
那么函数防抖有什么缺陷,从它的实现原理来看它的缺陷也是显而易见的。
比如我滚动页面要看看更多内容,用户肯定是希望边滚动边浏览到更多信息,到自己感兴趣的地方再停下来。函数节流的缺陷在于如果用户一直滚动,那么页面就一直不会刷新。对此我们需要更加人性化的函数节流来实现此效果。它会给用户操作添加一个最小时间片。待执行时间每隔最小时间片就会执行一次。它不会有原生scroll那么高的执行频率,同时也没有防抖函数那么僵硬。
同时它内部也使用到了防抖函数,可以说是防抖pro版本。
贴上代码。供童鞋们自行参悟其中奥妙。
const debounce = (realFn, timer = 200) => {
return (event) => {
if (!realFn.tid) {
realFn.tid = setTimeout(() => {
realFn(event)
clearTimeout(realFn.tid)
realFn.tid = null
}, timer)
}
}
}