關於性能優化的那點事——函數節流

函數節流

背景

javascript中的函數大多數狀況下都是由用戶主動調用觸發的, 除非是函數自己的實現不合理, 不然通常不會遇到跟性能相關的問題,但在少數狀況下, 函數的觸發不是由用戶直接控制的. 在這些場景下, 函數可能被很是頻繁調用, 而形成大的性能問題.javascript

場景

window.onresize事件
mousemove事件
scroll滾動事件
共同的特徵:高頻觸發事件函數, 若事件函數裏附帶DOM相關操做, 會形成很是大的性能消耗.java

原理

將即將被執行的函數使用setTimeout延遲一段時間執行, 若是該次延遲執行尚未完成, 則忽略接下來調用該函數的請求.設計模式

實現

示例1

underscore.js的函數節流定義: _.throttle(fn, wait, [options]);app

_.throttle接收三個參數, 第一次執行默認馬上執行一次fn
@params fn: 須要進行函數節流的函數;
@params wait: 函數執行的時間間隔, 單位是毫秒.
@params options: options有兩個選項, 分別是:
        {leading: false}: 第一次調用不執行fn
        {trailing: false}: 禁止最後一次延遲的調用


_.throttle = function(fn, wait, options) {
    var context,
        args,
        result,
        timeout = null,
        previous = 0;

    if(!options) {
        options = {};
    }

    var later = function() {
        previous = options.leading === false ? 0 : _.now();
        timeout = null;
        result = fn.apply(context, args);

        if(!timeout) {
            context = args = null;
        }
    };

    return function() {
        var now = _.now();
        if(!previous && options.leading === false) {
            previous = now;
        }

        var remaining = wait - (now - previous);
        context = this;
        args = arguments;

        if(remaining <= 0 || remaining > wait) {
            if(timeout) {
                clearTimeout(timeout);
                timeout = null;
            }
            previous = now;
            result = fn.apply(context, args);

            if(!timeout) {
                context = args = null;
            } else if(!timeout && options.trailing !== false) {
                timeout = setTimeout(later, remaining);
            }
            return result;
        }
    };
};

// demo:
$(window).scroll(_.throttle(function() {
    //相關處理
}, 500));

示例2

《javascript設計模式與開發實戰》中對函數節流示例:函數

throttle函數接收兩個參數
@params fn: 須要被延遲執行的函數;
@params interval: 延遲執行的時間;

var throttle = function(fn, interval) {
    var _self = fn,            // 保存須要被延遲執行的函數引用
        timer,                // 計時器
        firstTime = true;    // 是否第一次調用

    return function() {
        var args = arguments,
            _this = this;

        if(firstTime) {        // 若是是第一次調用, 不須要延遲執行
            _self.apply(_this, args);
            return firstTime = false;
        }

        if(timer) {            // 若是定時器還在, 說明前一次延遲執行還未完成
            return false;
        }

        timer = setTimeout(function() {
            clearTimeout(timer);
            timer = null;
            _self.apply(_this, args);
        }, interval || 500);
    };
};

// demo:
window.onresize = throttle(function() {
    console.log(1);
}, 500);
相關文章
相關標籤/搜索