函数节流和函数防抖浅析
函数防抖
函数防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。(也就是说只会执行最后一次)
例如:公交司机要在最后一个乘客上车后并且相应时间内再没有乘客上车才会触发关闭公交门的事件
实现方式:
通过计时器来实现,设置事件在n秒内只执行一次,如果在n秒内又触发了该事件,那么计时器清空。重新开始n秒计时,直到n秒内没有再触发该事件那么就进行计时器的函数回调
例子:如果一直滚动的话是不会触发scrollTap()函数的,只有最后一次并且500ms内没再滚动才会触发这个函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div style="height: 2000px;"></div>
</body>
</html>
<script>
var scrollTap = function () {
console.log('我执行了一次');
}
var debounce = (func, delay) => {
let timer;
return function () {
clearTimeout(timer); //每调用一次都清空之前的计时器
timer = setTimeout(() => {
func();
}, delay);
}
};
document.addEventListener('scroll', debounce(scrollTap, 500)); //500ms之内不触发滚动就执行scrollTap
</script>
补充:
浏览器在处理setTimeout和setInterval时,有最小时间间隔。
setTimeout的最短时间间隔是4毫秒;
setInterval的最短间隔时间是10毫秒,也就是说,小于10毫秒的时间间隔会被调整到10毫秒。
事实上,未优化时,scroll事件频繁触发的时间间隔也是这个最小时间间隔。
也就是说,当我们在debounce函数中的间隔事件设置不恰当(小于这个最小时间间隔),会使debounce无效。
函数节流
限制一个函数在一定时间内只能执行一次。也就是只要触发了相应事件,在这个事件规定的时间段内无论再触发多少次都只会执行最开始触发的那一次。
例子:过地铁进站口的时候,只要有一个人刷了卡,就会触发打开事件,其他人无论再刷多少次卡都不会再次触发打开时间,只有等待第一个刷卡的人时间过,闸门关闭才行
实现方式:
- 计时器:通过使用定时任务,延时方法执行。在延时的时间内,方法若被触发,则直接退出方法。从而,实现函数一段时间内只执行一次。
- 时间戳:其实现原理,通过比对上一次执行时间与本次执行时间的时间差与间隔时间的大小关系,来判断是否执行函数。若时间差大于间隔时间,则立刻执行一次函数。并更新上一次执行时间。
代码:
-
计时器:(如果重复滚动的话会没500ms执行一次scroll)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div style="height: 2000px;"></div> </body> </html> <script> var scrollTap = () => { console.log('我执行了一次函数'); } const throttle = (func, delay) => { let timer = null; return function () { if (timer) return; //如果函数正在被执行就退出此次请求 timer = setTimeout(() => { func(); timer = null; // clearTimeout(timer); 这个会导致timer为1 }, delay) //如果没有就开启新的计时器 } } document.addEventListener('scroll', throttle(scrollTap, 500)); //500ms执行一次 </script>
-
时间戳
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div style="height: 2000px;"></div> </body> </html> <script> var scrollTap = function () { console.log('我调用了一次'); } var throttle = (func, delay) => { var latTime = 0; return function () { const currentTime = new Date(); if (currentTime - latTime > delay) { //超过延迟时间就执行 func(); latTime = new Date(); } } } document.addEventListener('scroll', throttle(scrollTap, 1000)) </script>
异同比较
相同点:
- 都可以通过使用
setTimeout
实现。 - 目的都是,降低回调执行频率。节省计算资源。
不同点:
- 函数防抖,在一段连续操作结束后,处理回调,利用 clearTimeout 和 setTimeout 实现。函数节流,在一段连续操作中,每一段时间只执行一次,频率较高的事件中使用来提高性能。
- 函数防抖关注一定时间连续触发,只在最后执行一次,而函数节流侧重于一段时间内只执行一次。
常见应用场景
函数防抖的应用场景
连续的事件,只需触发一次回调的场景有:
- 搜索框搜索输入。只需用户最后一次输入完,再发送请求
- 手机号、邮箱验证输入检测
- 窗口大小Resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。
函数节流的应用场景
间隔一段时间执行一次回调的场景有:
- 滚动加载,加载更多或滚到底部监听
- 谷歌搜索框,搜索联想功能
- 高频点击提交,表单重复提交