(1)瀏覽器滾動事件scroll
(2) 鼠標的點擊事件 mouseup, mousedown,mousemove
(3) 鍵盤的keyup, keydown, input事件
(4)window的resize事件
以上四個場景中,事件都是連續觸發的,如果也連續執行相應的回調函數,不但浪費資源,而且沒有意義,還有可能導致瀏覽器或者服務器崩潰。
什么是函數節流和函數防抖
函數防抖:在一段連續操作結束后,處理回調(一次),利用 clearTimeout 和 setTimeout 實現。
應用場景: 窗口resize。 手機號、郵箱驗證輸入。搜索輸入。
函數節流:在一段連續操作中,每隔一段時間只執行一次。第一次調用函數,創建一個定時器,在指定的時間間隔之后運行代碼。當第二次調用該函數時,它會清除前一次的定時器並設置另一個。如果前一個定時器已經執行過了,這個操作就沒有任何意義。然而,如果前一個定時器尚未執行,其實就是將其替換為一個新的定時器。目的是只有在執行函數的請求停止了一段時間之后才執行。
應用場景:聯想搜索。滾動監聽加載。
函數節流的原理
函數節流的原理挺簡單的,那就是定時器。當我觸發一個事件時,先setTimout讓這個事件延遲一會再執行,如果在這個時間間隔內又觸發了事件,那我們就clear掉原來的定時器,再用setTimeout設一個新的定時器延遲一會執行。
例子場景:實現常見的搜索功能
1.沒有使用函數節流的情況下,為input綁定keyup事件處理函數,在控制台輸出我輸入的內容。
HTMl: <input id="search" type="text" name="search"> JS: <script> function queryData(text){ console.log("搜索:" + text); } var input = document.getElementById("search"); input.addEventListener("keyup", function(event){ queryData(this.value); }); </script>
測試結果:
可以看出,這種情況下,每按下一個鍵盤鍵,就輸出了一次。在性能上的消耗可想而知。
2.使用基本的函數節流模式的情況
HTML: <input id="search" type="text" name="search"> JS: <script> function queryData(text){ console.log("搜索:" + text); } var input = document.getElementById("search"); input.addEventListener("keyup", function(event){ throttle(queryData, null, 500, this.value); // queryData(this.value); }); function throttle(fn,context,delay,text){ clearTimeout(fn.timeoutId); fn.timeoutId = setTimeout(function(){ fn.call(context,text); },delay); } </script>
問題就是,假如我不斷地輸入,輸入了很多內容,但是我每兩次之間的輸入間隔都小於自己設置的delay值,那么,這個queryData搜索函數就一直得不到調用。
實際上,我們更希望的是,當達到某個時間值時,一定要執行一次這個搜索函數。所以,就有了函數節流的改進模式。
3.改進模式
HTML: <input id="search" type="text" name="search"> JS: <script> function queryData(text){ console.log("搜索:" + text); } var input = document.getElementById("search"); input.addEventListener("keyup", function(event){ throttle(queryData, null, 500, this.value,1000); // throttle(queryData, null, 500, this.value); // queryData(this.value); }); function throttle(fn,context,delay,text,mustApplyTime){ clearTimeout(fn.timer); fn._cur=Date.now(); //記錄當前時間 if(!fn._start){ //若該函數是第一次調用,則直接設置_start,即開始時間,為_cur,即此刻的時間 fn._start=fn._cur; } if(fn._cur-fn._start>mustApplyTime){ //當前時間與上一次函數被執行的時間作差,與mustApplyTime比較,若大於,則必須執行一次函數,若小於,則重新設置計時器 fn.call(context,text); fn._start=fn._cur; }else{ fn.timer=setTimeout(function(){ fn.call(context,text); },delay); } } </script>
測試部分轉載自:https://www.cnblogs.com/LuckyWinty/p/5949970.html