JavaScript函數節流(throttle)與函數去抖(debounce)

對於瀏覽器窗口大小改變的時候,來動態改變頁面元素的大小,能夠採用window的resize事件,實現代碼:javascript

<script type="text/javascript">
    var n = 0;
    function resizehandler(){
        console.log(new Date().getTime());
        console.log(++n);
    }
    
    window.onresize = resizehandler;
</script>

功能可以實現,都是當咱們用拖拽的方式改變瀏覽器大小的時候,控制檯會不斷打印執行resizehandler的函數的結果。java

一次簡單的拖拽會讓resizehandler()函數執行不少次,實際在顯示項目中resizehandler函數可能會很複雜,甚至會涉及到先後端的數據交互,因此一次拖拽執行不少次很明顯是不可以接受的。後端

函數去抖

其實咱們的本意只是窗口resize後頁面作一些調整就能夠了,而window的resize事件並非在resize結束後纔出發,具體的觸發頻率不是很清楚,但卻在不停地調用,直到窗口大小不在變化。相似的機制還有鼠標的mousemove,都是在短期內重複觸發。瀏覽器

在《JavaScript高級程序設計》中有專門應對此問題的函數防抖閉包

function throttle(method, context){
    clearTimeout(method.tId);
    method.tId = setTimeout(function(){
        method.call(context);
    }, 500);
}

原理很簡單,利用定時器,讓函數執行延遲500毫秒,在500毫秒內若是有函數又被調用則刪除上一次的調用,此次調用500毫秒後執行,如此往復。這樣剛纔的代碼能夠改成:app

<script type="text/javascript">
    var n = 0;
    function resizehandler(){
        console.log(new Date().getTime());
        console.log(++n);
    }
    
    function throttle(method, context){
        clearTimeout(method.tId);
        method.tId = setTimeout(function(){
           method.call(context);
        }, 500);
    }
    
    window.onresize = function(){
        throttle(resizehandler, window);
    };
</script>

這樣的話執行就沒有問題了。函數

函數防抖的另外一種方法

預先設定一個執行週期,當調用動做的時刻大於等於執行週期則執行該動做,而後進入下一個週期。性能

function throttle(method, dalay){
    var timer = null;
    return function(){
        var context = this, args = arguments;
        clearTimeout(timer);
        timer = setTimeout(function(){
            method.apply(context, args);
        }, delay);
    }
}

調用一下試試,同樣的效果this

<script type="text/javascript">
    var n = 0;
    function resizehandler(){
        console.log(new Date().getTime());
        console.log(++n);
    }
    
    function throttle(method, delay){
        var timer = null;
        return function(){
            var context = this, args = arguments;
            clearTimeout(timer);
            timer = setTimeout(function(){
                method.apply(context, args);
            }, delay);
        }
    }
    
    window.onresize = throttle(resizehandler, 500); //這裏由於返回函數句柄,不用包裝函數了。
</script>

比較

兩種方法都是利用了setTimeout,不一樣的是第二種方法加入的函數延遲執行時間,這個在第一種方案中很容易也具備的功能,無非是加一個參數。spa

可是第一種方案把tId設爲函數的一個變量保存,而第二種建立了閉包存儲。我的以爲差距不大,很喜歡第一種,簡單,高效。

新需求

百度首頁輸入自動提示同樣的東西,我在text上綁定keyup事件,每次鍵盤彈起的時候自動提示,可是又不想提示那麼頻繁,因而我用了上面方法,可是悲劇了,只有挺直輸入等500毫秒纔會提示,在輸入過程當中根本就沒有提示。看了一下代碼,可不是嘛,只有用戶會盲打,在500毫秒內按一下鍵盤,提示函數就會不斷被延遲,這樣只有停下來的時候纔會提示,這就沒有意義了。

能不能在函數節流的基礎上間隔固定時間就執行一次?

函數節流

在網上搜了一下咱們能夠根據第二種下發(第一種函數拓展多個變量感受有些很差)作些改動,添加一個參數做爲到固定間隔必須執行。

function throttle(method, delay, duration){
    var timer = null, begin = new Date();
    return function(){
        var context = this, args = arguments, current = new Date();
        clearTimeout(timer);
        if(current-begin >= duration){
            method.apply(context, args);
            begin = current;
        } else {
            timer = setTimeout(function(){
                method.apply(context, args);
            }, delay);
        }
    }
}

這樣每次咱們判斷間隔了夠久,要是超過設置時間則當即執行一次,以剛纔的例子試一試效果

window.onresize = throttle(resizehandler, 100, 200);

這樣,既沒有頻繁執行也沒有就最後執行。

總結

對於函數節流在作動態響應用戶行爲方面有較大的使用頻率,具體使用基礎版本的函數節流仍是改動版本的還要根據業務場景進行具體分析。

throttle和debounce均是經過減小實際邏輯處理過程的執行來提升事件處理函數運行性能的手段,並無實質上減小事件的觸發次數。 (逃)

image

相關文章
相關標籤/搜索