在DOM事件监听时,常见的mousemove、scroll、keydown、resize操作都可能导致在短时间内事件过于频繁的触发。如果在此时进行了DOM操作,极大可能导致浏览器运行崩溃。为了防止此类事件的发生,我们引入节流(throttle)与防抖(debounce)这一对概念
定义与应用场景
节流(throttle):减少过快的调用让一个函数不要执行的太频繁
防抖(debounce):对于一定时间段连续的函数调用,只让其执行一次
节流应用场景
- DOM 元素的拖拽功能实现(mousemove)
- 射击游戏的 mousedown/keydown 事件(单位时间只能发射一颗子弹)
- 计算鼠标移动的距离(mousemove)
- 搜索联想(keyup)
- 监听滚动事件判断是否到页面底部自动加载更多:给 scroll 加了 debounce 后,只有用户停止滚动后,才会判断是否到了页面底部;如果是 throttle 的话,只要页面滚动就会间隔一段时间判断一次
防抖应用场景
- 每次 resize/scroll 触发统计事件
- 文本输入的验证(连续输入文字后发送 AJAX 请求进行验证,验证一次就好)
防抖的实现
防抖的基本思路是,当触发事件,判断当前是否存在定时器。如果没有,则创建定时器。如果存在,则清楚之前的定时器,重新创建定时器。这样做保证了在固定间隔内多次触发事件,只会执行一次。
例子为模拟Ajax表单提交,规定点击间隔不能超过500ms
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <body> <p id="num">点击0次</p> <button>click</button> <script> function send() { let total = 0 let changeNum = () => { total++ document.querySelector('#num').textContent = `点击${total}次` } return changeNum }
function debounce(fn, wait) { let timer return function () { clearTimeout(timer) timer = setTimeout(() => { fn() }, wait) } }
let butt = document.querySelector('button') butt.addEventListener('click', debounce(send(), 500)) </script> </body>
|
节流的实现
节流实现的基本思路是,规定一个时间间隔,第一次触发立即执行。之后每一次触发时,判断上一次触发时间,若时间差小于间隔,则该次触发无效。若本次触发时间差大于间隔,则触发事件,并设置新的触发时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <body> <p id="num">点击0次</p> <button>click</button> <script> function send() { let total = 0 let changeNum = () => { total++ document.querySelector('#num').textContent = `点击${total}次` } return changeNum }
function throttle(fn, gap) { let last = 0 return function () { let curr = new Date() if (curr-last > gap){ fn() last = curr } } } let butt = document.querySelector('button') butt.addEventListener('click', throttle(send(), 500)) </script> </body>
|