函數防抖和函數分流


應用場景

我們經常需要監聽滾動條滾動或者鼠標的移動,但瀏覽器觸發這類事件的頻率非常高,可能在10幾毫秒就觸發一次,如果我們處理事件的函數需要操作大范圍的DOM,這對於瀏覽器的性能是個考驗,可能像chrome瀏覽器這樣優秀的瀏覽器會好一點,但放到老版本的IE下,就可能發生卡頓現象。有的時候,我們只需要處理函數執行一次,比如文本輸入驗證,執行多次處理函數反而沒有必要。

所以我們得想個辦法,減少DOM操作的頻度,也就是說稀釋處理函數的執行頻率,解決方法就是函數防抖和函數分流。函數防抖表示只執行一次處理函數,函數分流指降低處理函數的執行頻率,下面是具體解釋。

函數防抖

函數防抖指的是多次觸發事件后,事件處理函數只執行一次,而且是在事件觸發操作停止的時候。具體的思路就是延遲處理函數,如果設定的時間到來之前,又一次觸發了事件,就清除上一次的定時器,具體代碼實現如下。

var obj = document.getElementById('handle');
/**
 * 事件觸發的操作
 */
function myFun() {
    console.log('throttleV1');
}
/**
 * 不封裝的方法
 */
obj.onmousemove = function () {
    clearTimeout(myFun.timer);
    myFun.timer = setTimeout(myFun,50);
}

這里有一個保存timer的技巧,如果不保存timer,那么執行完事件處理函數后,timer將被銷毀,我們也就無法再清楚定時器了,所以需要保存這個變量,即使它所在的函數作用域對應的函數已經執行完了。這里用到的技巧是把timer設置為外部函數的屬性,這樣就不會被銷毀了。
我們還可以對這個方法進行封裝。

/**
 * 封裝的方法之幫頂函數
 * @param method
 * @param delay
 * @param context
 */
function debounce(method, delay, context) {
    clearTimeout(method.timer);
    method.timer = setTimeout(function () {
        method.call(context);
    },delay);
}
obj.onmousemove = function () {
    debounce(myFun,50);
};

這里涉及到了保存timer的技巧,總共有兩個方法。

  1. 設置為某個全局變量的屬性,例如綁定到某個全局函數上,問題是如果傳入了method是個匿名函數,綁定的timer就找不到了,所以這個方法有bug。
  2. 閉包 閉包的經典應用就是保留變量,下面是代碼實現。
/**
 * 封裝的方法之閉包
 * 閉包 如果想讓一個函數執行完后,函數內的某個變量(timer)仍舊保留,就可以使用閉包
 * 把要保存的變量在父作用域聲明,其他的語句放到子作用域里,並且作為一個function返回
 * 所以閉包可以理解為分離變量
 */
function debounce(method,delay) {
    var timer=null;
    return function () {
        var context = this, args = arguments;
        clearTimeout(timer);
        timer = setTimeout(function () {
            method.apply(context,args);
        },delay);
    }
}
obj.onmousemove = debounce(myFun,50);

函數分流

函數分流的思想就是計時,上面的代碼是只有在操作結束后才執行,只需要在上面的代碼上加一個計時判斷,如果超過了設定的時間,就執行一次處理函數,就達到了分流的效果。

/**
 * 函數節流throttle
 * @param 事件觸發的操作
 * @param 延遲執行函數的時間
 * @param 超過多長時間必須執行一次函數
 * @returns {Function}
 */
function throttle(method, delay, mustRunDelay) {
    var timer = null, args = arguments;
    var start = 0, now = 0;
    return function () {
        var context = this;
        now= Date.now();
        if(!start){
            start = now;
        }
        if(now - start >= mustRunDelay){
            method.apply(context, args);
            start = Date.now();
        }else {
            clearTimeout(timer);
            timer = setTimeout(function () {
                method.apply(context, args);
            }, delay);
        }

    }
}
obj.onmousemove = throttle(myFun, 50, 500);

總結

函數防抖和函數分流的思想都是通過定時器控制函數的執行頻率。

參考目錄

http://www.alloyteam.com/2012/11/javascript-throttle/
http://web.jobbole.com/88306/
https://github.com/hanzichi/underscore-analysis/issues/21
https://github.com/hanzichi/underscore-analysis/issues/22


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM