當某個事件頻繁觸發時,事件處理函數會頻繁執行,若是處理函數有一些費時、耗性能的操做,就會致使頁面出現卡頓甚至瀏覽器崩潰,這時就須要節流
和防抖
當事件頻發觸發時,事件處理程序每隔一段時間執行一次
若是某個頻繁觸發的事件在規定的時間內沒有再次觸發,則執行事件處理程序,若是在這段時間內事件再次觸發了,則從新計時
<!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、英雄聯盟、王者榮耀等須要頻繁點擊操做的場景
下拉加載更多,一直下拉,但一段事件內只執行一次事件處理程序(加載內容)