JavaScript 頻繁發射事件處理的優化 --- 函數節流/事件稀釋


引子:昨天面試時面試官問了如何實現一個固定導航欄,在我答完后面試官問我可能存在哪些問題,如何優化?

這個問題我答得不太好,但現在回想起來應該有兩個問題:

1. 把 fixbar元素 position:fixed 之后,它將脫離文檔流,后面的元素將會跟上,這可能會形成一個閃爍,解決方法是跟隨的元素設置 margin-top 為 fixbar 元素的高度,或者替換上一個等高的元素,這點面試時候沒有描述出來。

2. 就是這篇博文主要內容 ”函數節流“,英文名 throttle 函數,在一些庫,如underscore中有其實現,

主要思想是:對於非常頻煩發射的事件,可以設置一定時間的”緩沖“,在這一定的時間內只執行最后一次時間的響應函數。

例如對於 窗口的滾動條拖動,onscroll時間是非常頻繁發射的,如果每次發射都執行事件處理函數,那么將會增加瀏覽器負擔,這時如果200ms(只是舉例)執行一次,那么性能上將有所改善,效果上也能接受,當然實際應用需要二者找到一個平衡。

 

JQuery作者 John Resig 在2011年的一篇博文中提出了這個問題的最佳實踐方法,請點擊原文查看。代碼如下:

    var outerPane = $details.find(".details-pane-outer"),
        didScroll = false;
     
    $(window).scroll(function() {
        didScroll = true;
    });
     
    setInterval(function() {
        if ( didScroll ) {
            didScroll = false;
            // Check your page position and then
            // Load in more results
        }
    }, 250);

 

通俗地大家把它稱為 函數節流/事件稀釋, 在 underscore 中,也有這個實現:

throttle_.throttle(function, wait, [options]) 
Creates and returns a new, throttled version of the passed function, that, when invoked repeatedly, will only actually call the original function at most once per every waitmilliseconds. Useful for rate-limiting events that occur faster than you can keep up with.

By default, throttle will execute the function as soon as you call it for the first time, and, if you call it again any number of times during the wait period, as soon as that period is over. If you'd like to disable the leading-edge call, pass {leading: false}, and if you'd like to disable the execution on the trailing-edge, pass 
{trailing: false}.

var throttled = _.throttle(updatePosition, 100);
$(window).scroll(throttled);

以下是具體的實現代碼:

 _.throttle = function(func, wait, options) {
    var context, args, result;
    var timeout = null;
    var previous = 0;
    if (!options) options = {};
    var later = function() {
      previous = options.leading === false ? 0 : _.now();
      timeout = null;
      result = func.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) {
        clearTimeout(timeout);
        timeout = null;
        previous = now;
        result = func.apply(context, args);
        if (!timeout) context = args = null;
      } else if (!timeout && options.trailing !== false) {
        timeout = setTimeout(later, remaining);
      }
      return result;
    };
  };

 

參考鏈接:

Learning from Twitter

underscore.js


免責聲明!

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



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