函数防抖,函数节流(站在巨人的肩膀上)
目录:
1、概念解析
1.1 函数防抖(debounce)
1.2 函数节流(throttle)
2、正常回调,函数防抖,函数节流的执行情况比较
3、实现原理及应用
3.1 函数防抖实现原理及实例
3.2 函数节流实现原理及实例
4、异同比较
相同点
不同点
在前端开发的过程中,我们经常会需要绑定一些持续触发的事件,如 resize、scroll、mousemove 等等,但有些时候我们并不希望在事件持续触发的过程中那么频繁地去执行函数。
通常这种情况下我们怎么去解决的呢?一般来讲,防抖和节流是比较好的解决方案。
1、概念解析
1.1函数防抖(debounce)
-
- 定义
函数防抖,就是指触发时间后的n秒内函数只能执行一次,如果在n秒内又触发了事件,则会重新计算函数执行时间。
简单的说,当一个动作连续触发,则只执行最后一次。
也就是说,当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才执行一次,如果设定的事件到来之前,又触发了事件,就重新开始延时。
-
- 应用场景
- 搜索框搜索输入,只需用户最后一次输入完再发送请求。
- 手机号、邮箱验证输入检测。
- 窗口大小Resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。
1.2 函数节流(throttle)
-
- 定义
限制一个函数在一定时间内只能执行一次。
-
- 应用场景
- 加载更多、滚到底部监听
- 高频点击提交,表单重复提交
2.正常回调,函数防抖,函数节流的执行情况
下图是通过可视化工具(debounce & throttle demo (nimius.net)),对鼠标移动(mousemove)时间回调的执行情况。(遗憾不会带鼠标动作的截图)
注释:竖线的疏密代表时间执行的频繁程度。
- 正常情况下,竖线非常密集,函数执行的很频繁。
- debounce(函数防抖)很稀疏,只有当鼠标停止移动时,才会执行一次。
- throttle(函数节流)分布的较为均匀,每过一段时间就会执行一次。
3、实现原理及应用
3.1函数防抖实现原理
// 实现函数防抖的函数 const debounce = function(fn,wait){ let timer; return function(){ timer && clearTimeout(timer); let that = this,args = arguments; timer = setTimeout(function(){ fn.apply(that,args); },wait || 1000); }; }; //测试(只执行最后一次) let inter = setInterval(debounce(function(){ console.log('hello world!') }),100) seTimeout(function(){ clearInterval(inter); })
函数防抖在执行目标方法时,会等待一段时间。当又执行相同方法时,若前一个定时任务为执行完,则clear掉定时任务,重新定时。
3.1.1 js原生实现函数防抖
实现功能:用户最后一次点击按钮设定的时间2s后控制台才会输出当前时间戳。
如果不使用函数防抖的正常情况下,用户点击按钮控制台依次输出当前时间戳。
<!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>函数防抖test</title> </head> <body> <button id="btn">防抖代码实现</button> <script type="text/javascript"> function debounce(fn,delay){ //记录上一次的延时器 var timer = null; return function(){ //清除上一延时器 clearTimeout(timer) timer = setTimeout(function(){ fn.apply(this) },delay) } } document.getElementById('btn').onclick = debounce(function(){ console.log('点击时间被触发了' + Date.now()) },2000) </script> </body> </html>
实现效果:
3.1.2 vue中实现函数防抖实例(可以注册全局)
<!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>在vue项目中实现函数防抖test</title> </head> <body> <div id="app"> <p>{{message}}</p> <button id="btn" @click='fn()'>在vue项目中实现函数防抖test</button> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.js"></script> <script type="text/javascript"> const delay = (function(){ let timer = 0 return function(callback,ms){ clearTimeout(timer) timer = setTimeout(callback,ms) } })()
new Vue({ el:"#app", data(){ return { message:'防抖实验' } }, methods:{ fn(){ delay(() =>{ //执行部分 console.log('在vue项目中实现函数防抖test---' + Date.now()) },500) } } }) </script> </body> </html>
3.2函数节流实现原理
//实现函数节流的函数 const throttle = function(fn,wait){ let canrun = true; return function(){ if(!canrun) return false; canrun = false; let that = this, args = arguments; setTimeout(function(){ fn.apply(that,args); canrun = true; }, wait || 1000); }) } //测试(原本100ms执行一次,现在3000ms) setInterval(throttle(function() { console.log("hello world") },3000),100)
函数节流的目的,是为了限制函数一段时间内只能执行以西。因此,通过使用定时任务,延时方法执行。再延时的时间内,方法若被出发,则直接推出方法。从而,实现函数一段时间内只执行一次。
3.2.1 js原生实现函数节流
<!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>函数节流test</title> </head> <body> <div style="height: 1000px;background:red;width: 100px;"></div> <script type="text/javascript"> function throttle(fn,delay){ //fn:要被节流的函数;delay:规定时间 //记录上一次触发的时间 var lasttime = 0; //通过闭包的方式使用lasttime变量,每次都是上次的时间 return function(){ var nowtime = Date.now(); if(nowtime - lasttime > delay){ //修正this函数问题 fn.call(this); //同步时间 lasttime = nowtime; } } } document.onscroll = throttle(function(){ console.log('点击时间被触发了' + Date.now()) },2000) </script> </body> </html>
3.1.2 vue中实现函数节流实例
实现效果:持续触发scroll事件时,并不立即执行handle函数,每隔1000毫秒才会执行一次handle函数。
<!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>在vue项目中实现函数防节流test</title> </head> <body> <div id="app"> <div style="height: 1000px;width:200px;background: red;">{{message}}</div> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.js"></script> <script type="text/javascript"> const throttle = function(func,delay){ var timer = 0; return function(){ var context = this; var args = arguments; if(!timer){ timer = setTimeout(function(){ func.apply(context,args); timer = null; },delay); } } } new Vue({ el:"#app", data(){ return { message:'节流实验,滚动后出现节流打印' } }, created(){ window.addEventListener('scroll', throttle(this.handle, 1000)); }, methods:{ handle(){ console.log('在vue项目中实现函数节流test---' + Date.now()) } } }) </script> </body> </html>
4、异同比较
相同点
-
-
- 都可以通过使用seTimeout实现
- 目的都是降低回调执行频率。节省计算资源。
-
不同点
-
-
- 函数防抖:在一段连续操作结束后,处理回调,利用clearTimeout和setTimeout实现。函数节流:在一段连续操作中,每一段时间只执行一次,在频率较高的事件中可以用来提高性能。
- 函数防抖关注一定时间连续触发,只在最后执行一次,而函数节流侧重于一段时间内只执行一次。
-
防抖、节流函数封装: https://www.cnblogs.com/meiyanstar/p/14816452.html