高級函數技巧-函數防抖與節流

在實際開發中,函數必定是最實用最頻繁的一部分,不管是以函數爲核心的函數式編程,仍是更多人選擇的面向對象式的編程,都會有函數的身影,因此對函數進行深刻的研究是很是有必要的。javascript


函數節流

比較直白的說,函數節流就是強制規定一個函數在一段時間內可以被執行的最大次數,好比,規定某個函數在每100毫秒的時間段內,最多被執行一次,那麼對應的在10s(10000ms)內,最多就會執行100(10000ms/100ms)次css

這裏節流和防抖的概念比較容易搞混,因此文中許多關鍵性定義,參考此處文檔翻譯過來,移步到the-difference-between-throttling-and-debouncinghtml

在瀏覽器中,頻繁的DOM操做很是消耗內存和CPU時間,好比監聽了resize,touchmove,scroll...等事件,在dom改變時都會不斷觸發回調。如今的react 和 vue 等前端框架都提出了虛擬DOM的概念,會把屢次DOM操做合併到一次真實操做中,就是使用了Diff算法,這樣就大大減低了DOM操做的頻次。可是,這裏並非要討論diff算法,若是感興趣能夠戳上面的連接,而是解釋如何利用setTimeout來減低DOM頻繁操做的風險。前端

最先接觸到這個概念的時候,是在《高程3》最後幾章上面。vue

函數節流背後的基本思想是指,某些代碼不能夠在沒有間斷的狀況下連續重複執行.第一次調用函數,建立了一個定時器,在指定的時間間隔以後運行代碼。當第二次調用該函數時,它會清除前一次的定時器並設置另外一個。

封裝方法也比較簡單,書中對此問題也進行了處理:java

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

使用定時器,讓函數延遲1秒後執行,在此1秒內,而後throttle函數再次被調用,則刪除上次的定時器,取消上次調用的隊列任務,從新設置定時器。這樣就能夠保證1秒內函數只會被觸發一次,達到了函數節流的目的react

能夠利用 resize事件測試一下;git

var  i = 0;
   function handler(){
       console.log(i++);
    }
   window.onresize = function(){
       throttle(handler,window)
   }

能夠發現,在瀏覽器的調試模式下,切換橫屏/豎屏,只觸發了一次github

chrome 調試

函數防抖

函數防抖 規定函數再次執行須要知足兩個條件:ajax

  • 1,必需要等待一段時間;
  • 2,在條件1等待的時間段內再也不被觸發,一旦在條件1等待的時間內再次被觸發,等待時間就要從新開始計算。

好比:對一個函數加了100ms的防抖操做,而後在3s(3000ms)時間段內,這個函數被不連續的調用了1000次,3s後中止調用。 它只會在3100ms的時刻執行一次。

具體實現代碼,看下 underscore.js中的 _.debounce 源碼:

// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
_.debounce = function(func, wait, immediate) {
  var timeout, args, context, timestamp, result;

  var later = function() {
 var last = _.now() - timestamp;

 if (last < wait && last >= 0) {
   timeout = setTimeout(later, wait - last);
 } else {
   timeout = null;
   if (!immediate) {
     result = func.apply(context, args);
     if (!timeout) context = args = null;
   }
 }
  };

  return function() {
   context = this;
   args = arguments;
   timestamp = _.now();
   var callNow = immediate && !timeout;
   if (!timeout) timeout = setTimeout(later, wait);
   if (callNow) {
     result = func.apply(context, args);
     context = args = null;
   }

   return result;
  };
};

wait參數表明debounce時間, _.now()返回當前時間的時間戳,一樣以ms 爲單位。 若是傳入了 immediate,會當即觸發回調函數。

應用場景:

參考資料:

相關文章
相關標籤/搜索