当某个事件频繁触发时,事件处理函数会频繁执行,若是处理函数有一些费时、耗性能的操做,就会致使页面出现卡顿甚至浏览器崩溃,这时就须要节流
和防抖
当事件频发触发时,事件处理程序每隔一段时间执行一次
若是某个频繁触发的事件在规定的时间内没有再次触发,则执行事件处理程序,若是在这段时间内事件再次触发了,则从新计时
<!DOCTYPE html> <html lang = "en"> <style> #wrap .scrollBox, #wrap .list{ float: left; } .scrollBox { width: 40%; height: 500px; margin-right: 5%; border: 1px solid red; overflow: scroll; } .list { width: 40%; border: 1px solid red; } </style> <body> <div id = "wrap"> <div class = "scrollBox"><!-- 内容动态生成 --></div> <ul class = "list"> <li>我是列表</li> </ul> </div> </body> <script> // 防抖函数,不如不理解为何能防抖,请看下面的解释内容 function debounce (handle, delay) { let timer = null return function () { if (timer) { // console.log('clearTimeout, clear timer') clearTimeout(timer) } // console.log('setTimeout, set timer') timer = setTimeout(handle, delay) } } // 事件处理函数,生成一个li节点,并添加到ul节点的末尾 function handle () { const node = document.createElement('li') const text = document.createTextNode(Date.now()) node.appendChild(text) document.getElementsByClassName('list')[0].appendChild(node) } // 添加事件监听器,造成了一个闭包 document.getElementsByClassName('scrollBox')[0].addEventListener('scroll', debounce(handle, 500)) // 生成滚动栏的内容,如下部分和上面的html、css只为构造一个实验环境 let content = '' for (let i = 0; i < 1000; i++) { content += '在我上面滚动鼠标' } const textNode = document.createTextNode(content) document.getElementsByClassName('scrollBox')[0].appendChild(textNode) </script> </html>
debounce
函数为何会有防抖的功能呢?javascript
document.getElementsByClassName('scrollBox')[0].addEventListener('scroll', debounce(handle, 500))
,这行代码造成了一个闭包,debounce
函数运行之后返回一个函数,而返回的这个函数在scroll事件触发期间(即一直滚动页面)其实一直在不停的执行(能够放开注释掉的两行console.log()
进行测试),好比:第一次开始滚动,第一次执行到debounce
返回的函数,发现!!timer === false
,if
条件不成立,直接执行后面的setTimeout
,设置timer
,可是鼠标在一直滚动,立刻debounce
返回的函数又在执行第二次了,发现timer
不为空,if
条件成立,执行clearTimeout
清除timer
,可是这时你会发现刚才设置的定时任务尚未执行,就被清除了,这就达到了防抖的要求,鼠标一直滚动一直重复上面的过程(函数一直在执行),直到中止滚动,中止前一次设置的定时任务不会被清除,规定的延时时间一到,执行handle
(处理程序),下一次开始滚动,清除上一次的timer,接着重复上面的过程
有了防抖的基础,节流这里,就省略掉构建实验环境的html代码了,直接上节流方法的实现,有两种实现方式,分别是时间戳和定时任务
定时任务css
// 节流函数 function throttle (handle, delay) { // 节流的关键点在于timer,timer被赋值之后,只有在定时任务执行之后,timer才会从新被置为null let timer = null return function () { if (!timer) { // 设置timer timer = setTimeout(() => { handle() // 定时任务执行完毕,置空timer timer = null }, delay) } } } // 事件处理函数 function handle () { console.log('I am handle function') } // 添加事件监听器 document.getElementById('app').addEventListener('scroll', throttle(handle, 1000))
时间戳html
// 节流函数 function throttle (handle, delay) { // 节流的关键所在 let prevTime = Date.now() return function () { if (Date.now() - prevTime >= delay) { // 时间到了之后执行事件处理程序,并重置prevTime handle() prevTime = Date.now() } } } // 事件处理函数 function handle () { console.log('I am handle function') } // 添加事件监听器 document.getElementById('app').addEventListener('scroll', throttle(handle, 1000))
都是限制事件处理程序的执行频率
节流: 定时重复执行事件处理程序,不论事件触发有多频繁
防抖:只有事件最后一次触发(指定时间内没有再次触发)才会执行处理程序
input
事件的操做只有内容输入完成之后,才执行最后的事件处理程序(好比:搜索内容、验证表单内容)
窗口resize中止后,最后执行一次事件处理程序
游戏,好比打地鼠、CS、英雄联盟、王者荣耀等须要频繁点击操做的场景
下拉加载更多,一直下拉,但一段事件内只执行一次事件处理程序(加载内容)