首先自己理解的概念:
防抖函數:防抖按照我的理解就是不管你觸發多少次,都等到你最後觸發後過一段你指定的時間才觸發。
節流函數:節流就是,不管怎麼觸發,都是按照指定的間隔來執行。
實例:
防抖函數例子:
基礎版本:
現在有個要求就是剛開始的時候也觸發,最後一次也觸發,並且可以配置,先寫個測試頁面方便測試功能,每次按空格鍵就會讓數字加1,來測試防抖和節流函數。
通過 leading 和 trailing 兩個參數來決定開始和結束是否執行,如果 leading 爲 true,則沒次按空格都會執行一次,如果 trailing 爲 true,則每次結束都會將最後一次觸發執行。以防抖函數距離,如果兩者都爲 true,則第一次按空格會加 1,然後快速按空格,此時裏面的 getUserAction 並不會執行,而是等到鬆手後再執行,加入 trailing 爲 false,則鬆手後不會執行。
解釋一下,每次記錄上次調用的時間,與現在的時間對比,小於間隔的話,第一次執行後之後就不會執行,大於間隔或在間隔時間後調用了,則重置 flag,可以與上面那個基本版的對比着看。
函數防抖的應用場景,最常見的就是用戶註冊時候的手機號碼驗證和郵箱驗證了。只有等用戶輸入完畢後,前端才需要檢查格式是否正確,如果不正確,再彈出提示語。以下還是以頁面元素滾動監聽的例子,來進行解析:
// 函數防抖
var timer = false;
document.getElementById("debounce").onscroll = function(){
clearTimeout(timer); // 清除未執行的代碼,重置回初始化狀態
timer = setTimeout(function(){
console.log("函數防抖");
}, 300);
};
函數防抖的要點,也是需要一個setTimeout
來輔助實現。延遲執行需要跑的代碼。
如果方法多次觸發,則把上次記錄的延遲執行代碼用clearTimeout
清掉,重新開始。
如果計時完畢,沒有方法進來訪問觸發,則執行代碼。
這個方法的作用是監聽ID爲
debounce
元素的滾動事件
進入滾動事件方法體的時候,做的第一件事就是清除上次未執行的setTimeout
。而setTimeout
的引用id由變量timer
記錄。
clearTimeout
方法,允許傳入無效的值。所以這裏直接執行clearTimeout
即可。
然後,將需要執行的代碼放入setTimeout
中,再返回setTimeout
引用給timer緩存。
如果倒計時300ms
以後,還沒有新的方法觸發滾動事件,則執行setTimeout
中的代碼。
函數防抖的實現重點,就是巧用setTimeout
做緩存池,而且可以輕易地清除待執行的代碼。
其實,用隊列的方式也可以做到這種效果。這裏就不深入了。
函數防抖:註冊時郵箱的輸入框,隨着用戶的輸入,實時判斷郵箱格式是否正確。此時,每一次的用戶輸入都觸發郵箱格式檢測事件,造成了浪費,於是設置兩次輸入之間的時間間隔大於800ms時(用戶結束輸入時),再執行檢查郵箱格式。
- const filter = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
- $("#email").on("keyup",checkEmail());
- function checkEmail(){
- let timer=null;
- return function (){
- clearTimeout(timer);
- timer=setTimeout(function(){
- console.log('執行檢查');
- },800);
- }
- }
假如只過了100ms,上次的定時還沒執行,此時清除定時,重新定時800ms。
緊跟着又來了一次輸入,上次定時依然沒執行,再次清除定時,重新定時800ms。
...
直到最近一次的輸入,後面沒有緊鄰的輸入了,這最近一次的輸入定時計時結束,終於執行了檢查代碼。
結果是:如果兩次輸入觸發事件的時間間隔不足800ms的,不執行檢查代碼。 兩次觸發事件的時間間隔大於800ms了,則前面的執行檢查。
這就是傳說中的 函數防抖。
節流
節流就是,不管怎麼觸發,都是按照指定的間隔來執行,同樣給個基本版。
同樣和防抖函數一樣加上兩個參數,也可使用上面的例子來測試,其實兩者的代碼很類似。
函數節流:一個加載新聞的列表頁,只要滾動到頁面的最下面就繼續加載一部分新聞出來,即滾動加載。這時,就要監聽滾動事件,判斷若滾動
到頁面底部了,就執行加載新聞的代碼。
- $(window).scroll(loadMore());
- function loadMore(){
- var canRun=true;
- return function(){
- if(!canRun){return;}
- canRun=false;
- setTimeout(function(){
- console.log("執行滾動事件");
- var docHeight=$(document).height();
- var winHeight=$(window).innerHeight();
- var scrollDistance=$(window).scrollTop();
- if( docHeight - (winHeight+scrollDistance) <=100 ){
- console.log('加載中...');
- }
- canRun=true;
- },600);
- }
- }
於是設置一個開關,一次只能有一個觸發執行,並對執行設置計時一段時間再執行,執行完畢之後再解鎖。這就是函數節流。
函數節流應用的實際場景,多數在監聽頁面元素滾動事件的時候會用到。因爲滾動事件,是一個高頻觸發的事件。以下是監聽頁面元素滾動的示例代碼:
// 函數節流
var canRun = true;
document.getElementById("throttle").onscroll = function(){
if(!canRun){
// 判斷是否已空閒,如果在執行中,則直接return
return;
}
canRun = false;
setTimeout(function(){
console.log("函數節流");
canRun = true;
}, 300);
};
函數節流的要點是,聲明一個變量當標誌位,記錄當前代碼是否在執行。
如果空閒,則可以正常觸發方法執行。
如果代碼正在執行,則取消這次方法執行,直接return
。
這個方法的作用是監聽ID爲
throttle
元素的滾動事件。
當canRun
爲true
,則代表現在的滾動處理事件是空閒的,可以使用。
通過關卡if(!canRun)
,等於就拿到了通行證。然後下一步的操作就是立馬將關卡關上canRun=false
。這樣,其他請求執行滾動事件的方法,就被擋回去了。
接着用setTimeout
規定最小的時間間隔300,接着再執行setTimeout
方法體裏面的內容。
最後,等setTimeout
裏面的方法都執行完畢,才釋放關卡canRun=true
,允許下一個訪問者進來。
這個函數節流的實現形式,需要注意的是執行的間隔時間是>=300ms
。如果具體執行的方法是包含callback
的,也可以將canRun=true
這一步放到callback
中。理解了函數節流的關卡設置重點,其實改起來就簡單多了。
demo地址:https://wall-wxk.github.io/blogDemo/2017/02/15/throttleAndDebounce.html