函數防抖和函數分流

應用場景

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

因此咱們得想個辦法,減小DOM操做的頻度,也就是說稀釋處理函數的執行頻率,解決方法就是函數防抖和函數分流。函數防抖表示只執行一次處理函數,函數分流指下降處理函數的執行頻率,下面是具體解釋。java

函數防抖

函數防抖指的是屢次觸發事件後,事件處理函數只執行一次,並且是在事件觸發操做中止的時候。具體的思路就是延遲處理函數,若是設定的時間到來以前,又一次觸發了事件,就清除上一次的定時器,具體代碼實現以下。git

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設置爲外部函數的屬性,這樣就不會被銷燬了。
咱們還能夠對這個方法進行封裝。github

/**
 * 封裝的方法之幫頂函數
 * @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的技巧,總共有兩個方法。web

  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);

函數分流

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

/**
 * 函數節流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閉包

相關文章
相關標籤/搜索