函数防抖
- 定义:触发事件后,在n秒内函数只能执行一次,如果触发事件后在n秒内又触发了事件,则会重新计算函数延长执行的时间。
- 个人总结:使重复执行的函数在短时间内只执行最后一次。
- 实现:
- 准备一个定时器,把要做的事情放在定时器里;
- 判断是否存在定时器,没有就开始计时,有就重新计时;
- 一直重新计时,直到没有操作的时候就只有一个定时器,所以只执行一次;
- 举个栗子
- 没有使用防抖的时候
html<!-- 给div高度撑出滚动条 --> <div style="height: 8000px;width: 100px;"></div> <script> const showLocaltion = () => { // 获取滚动条位置(兼容写法) var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop // 没有防抖的时候按方向键也会执行7、8次 console.log('滚动条位置:', scrollTop) } // 事件监听 window.addEventListener('scroll',showLocaltion) </script>- 使用防抖
html<!-- 滚动条不停滚动,只打印一次滚动条位置--> <div style="height: 8000px;width: 100px;"></div> <script> const showLocaltion = () => { // 获取滚动条位置(兼容写法) var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop // 按方向键也会执行7、8次 console.log('滚动条位置:', scrollTop) } // 事件监听 // 存储定时器ID let timer = null window.addEventListener('scroll', () => { if (timer) { // 有定时器就先清除再重新开始计时 clearTimeout(timer) timer = setTimeout(showLocaltion, 1000) } else { // 没有定时器就直接开始计时 timer = setTimeout(showLocaltion, 1000) } }) </script>- 因为得有个外层变量存储定时器ID,所以也可以使用闭包写法
html<div style="height: 8000px;width: 200px;"></div> <script> const showLocaltion = () => { // 获取滚动条位置(兼容写法) var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop // 按方向键也会执行7、8次 console.log('滚动条位置:', scrollTop) } function debounce(fn, delay) { let timer = null return function () { if (timer) { clearTimeout(timer) timer = setTimeout(fn, delay) } else { timer = setTimeout(fn, delay) } } } window.onscroll = debounce(showLocaltion, 1000) </script>
函数节流
定义:如果短时间内大量触发同一事件,那么在事件处理函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效。
个人总结:使重复执行的函数每隔一段时间执行一次。
实现:
- 准备一个开关,事件处理程序根据开关来决定是否执行
- 开始执行函数之前关闭开关
- 每隔一段时间之后就打开开关
举个栗子
html<!-- 函数节流 --> <!-- 滚动条不停滚动,每隔一秒打印一次位置 --> <!-- 准备一个开关,事件处理程序根据开关来决定是否执行 --> <!-- 撑出滚动条 --> <div style="height: 8000px;width: 100px;"></div> <script> const showLocaltion = () => { var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop console.log('滚动条位置:', scrollTop) // 隔段事件后打开开关,下次才能进入该分支 isGo = true } // 事件监听 // 开关 var isGo = true window.addEventListener('scroll', () => { if (isGo) { // 关掉开关 isGo = false setTimeout(showLocaltion, 1000) } else { return } }) </script>- 闭包写法
html<div style="height: 8000px;width: 200px;"></div> <script> const showLocaltion = () => { var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop console.log('滚动条位置:', scrollTop) // 隔段事件后打开开关,下次才能进入该分支 isGo = true } function throttle(fn, delay) { let isGo = true return function () { if (isGo) { // 关掉开关 isGo = false setTimeout(()=>{ fn() // 隔段事件后打开开关,下次才能进入该分支 isGo = true }, delay) }else{ return } } } window.onscroll = throttle(showLocaltion, 1000) </script>
应用场景
节流(throttle)
- DOM元素的拖拽功能实现(mousemove)
- 搜索联想(keyup)
- 计算鼠标移动的距离(mousemove)
- Canvas 模拟画板功能(mousemove)
- 射击游戏的 mousedown/keydown 事件(单位时间只能发射一颗子弹)
- 监听滚动事件判断是否到页面底部自动加载更多:给 scroll 加了 debounce 后,只有用户停止滚动后,才会判断是否到了页面底部;如果是 throttle 的话,只要页面滚动就会间隔一段时间判断一次
防抖(debounce)
- scroll事件滚动触发事件
- 搜索框输入查询,如果用户一直在输入中,没有必要不停地调用去请求服务端接口,等用户停止输入的时候,再调用,设置一个合适的时间间隔,有效减轻服务端压力。
- 表单验证
- 按钮提交事件。
- 浏览器窗口缩放,resize事件(如窗口停止改变大小之后重新计算布局)等。