防抖與節流

zlmdb發表於2019-02-18

防抖

防抖技術即是可以把多個順序地呼叫合併成一次,也就是在一定時間內,規定事件被觸發的次數。

通俗一點來說,看看下面這個簡化的例子:

// 簡單的防抖動函式
function debounce(func, wait, immediate) {
    // 定時器變數
    var timeout;
    return function() {
        // 每次觸發 scroll handler 時先清除定時器
        clearTimeout(timeout);//清除掉上一次的定時器回撥函式,上次事件也就不會執行了。在500ms內,再次滾動,那上次的就清除掉了,不會執行。
        // 指定 xx ms 後觸發真正想進行的操作 handler
        timeout = setTimeout(func, wait);
    };
};

// 實際想繫結在 scroll 事件上的 handler
function realFunc(){
    console.log("Success");
}
複製程式碼

採用了防抖動

window.addEventListener('scroll',debounce(realFunc,500));

封裝上面的函式,更簡潔

防抖動函式

function debounce(func, wait, immediate) {
    var timeout;
    return function() {
        var context = this, args = arguments;
        var later = function() {
            timeout = null;
            if (!immediate) func.apply(context, args);
        };
        var callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
    };
};

var myEfficientFn = debounce(function() {
    // 滾動中的真正的操作
}, 250);

// 繫結監聽
window.addEventListener('resize', myEfficientFn);
複製程式碼

防抖:

注意:上面的500ms,意思是:兩次滾動事件的相隔時間必須大於500ms才能觸發成功,而且是倒數第二次的事件觸發成功的。如果所有的事件觸發的時間間隔都小於500ms,那麼就不會觸發事件回撥函式了,只有最後一次的事件回撥能成功執行。
複製程式碼

節流(Throttling)

  • 防抖函式確實不錯,但是也存在問題,譬如圖片的懶載入,我希望在下滑過程中圖片不斷的被載入出來,而不是隻有當我停止下滑時候,圖片才被載入出來。又或者下滑時候的資料的 ajax 請求載入也是同理。

  • 這個時候,我們希望即使頁面在不斷被滾動,但是滾動 handler 也可以以一定的頻率被觸發(譬如 250ms 觸發一次),這類場景,就要用到另一種技巧,稱為節流函式(throttling)。

  • 節流函式,只允許一個函式在 X 毫秒內執行一次。
  • 與防抖相比,節流函式最主要的不同在於它保證在 X 毫秒內至少執行一次我們希望觸發的事件 handler。

  • 與防抖相比,節流函式多了一個 mustRun 屬性,代表 mustRun 毫秒內,必然會觸發一次 handler ,同樣是利用定時器,看看簡單的示例:

  	// 簡單的節流函式
	function throttle(func, wait, mustRun) {
	    var timeout,
	        startTime = Date.parse(new Date());//重點,這裡是在頁面載入後就執行的。不是等到滾動才執行。

	        console.log(startTime);

	    return function() {
	        var context = this,
	            args = arguments,
	            curTime = new Date();

	        clearTimeout(timeout);
	        // 如果達到了規定的觸發時間間隔,觸發 handler
	        if(curTime - startTime >= mustRun){
	            func.apply(context,args);
	            startTime = curTime;//重置開始時間startTime,把它設定成當前的時間
	        // 沒達到觸發間隔,重新設定定時器
	        }else{
	            timeout = setTimeout(func, wait);
	        }
	    };
	};
	// 實際想繫結在 scroll 事件上的 handler
	function realFunc(){
	    console.log("Success");
	}
	// 採用了節流函式
	window.addEventListener('scroll',throttle(realFunc,500,1000));
複製程式碼
上面簡單的節流函式的例子可以拿到瀏覽器下試一下,大概功能就是如果在一段時間內 scroll 觸發的間隔一直短於 500ms ,那麼能保證事件我們希望呼叫的 handler 至少在 1000ms 內會觸發一次。

注意:強調這句話:保證事件我們希望呼叫的 handler 至少在 1000ms 內會觸發一次。最多也是一次。也就是說,在所有滾動事件的每兩個相隔時間都加起來,從開始到結束滾動,每隔1000ms就是觸發一次。比如從開始到結束,共經歷1分鐘,每隔1秒呼叫一次,總共大概呼叫60次。
複製程式碼

參考連結:

www.cnblogs.com/fangdongdem…

部落格專題裡的 github.com/mqyqingfeng…

相關文章