天天看點

javaScript中的函數節流與函數防抖

javaScript中的函數節流與函數防抖

問題:在實際開發中我們很可能用到resize和mousemove等事件,但是這些會頻繁的觸發,如果是在對應的事件處理函數寫一些操作dom的方法的話或者說發送一些ajax請求的話,這樣肯定非常耗性能,并且給使用者的體驗也不好。

函數節流

函數節流的原理:持續觸發一個事件,每隔一段的時間,事件隻會執行一次。simplely speaking,就像一個水龍頭,我們把它關得很小,流水事件一直在觸發,但是是每隔一定的事件才滴出一滴水滴。

實作方式: 一般有兩種,第一就是使用時間戳,第二就是使用setTimeout定時器。

使用時間戳方式:當我們觸發事件的時候,取出目前的時間戳,求出目前時間戳和前一次執行的時間戳的內插補點,如果內插補點大于我們設定的時間間隔,那麼就執行該動作,并更新執行時間,否則,就不執行該動作。

//《JavaScript進階程式設計》
 //節流函數
 function throttle(method,context){
     //清除上一次的定時器
     clearTimeout(method.tId);
     //重新設定定時器,設定目前周期之後調用
     method.tId = setTimeout(function(){
         //這裡是真正要執行的操作
         method.call(context);
     },);
 }

 //處理函數
 function handOpe(){
     /*do something what you want*/
 }

 //regist the aciton at the object named window
 window.onresize = function(){
     throttle(handOpe,window);
 }
           

使用定時器方式:使用定時器的原理就是,當事件觸發的時候,檢查定時器是否存在,如果存在說明函數還沒有執行(因為函數執行中我們清空了timeoutId)是以這個時候我們就不執行,等它執行,并清空定時器,這個時候就可以設定下一個定時器了。

compare

  • 第一種(時間戳)事件會立即執行,第二種事件會在wait秒後執行
  • 第一種事件停止後沒有辦法再執行,第二種事件停止後依然會再執行一次事件函數。

函數防抖

函數防抖 :如果用手指一直按住一個彈簧,它将不會彈起直到你松手為止。也就是說當調用動作n毫秒後,才會執行該動作,若在這n毫秒内又調用此動作則将重新計算執行時間。

原理:通俗的将就是你盡管觸發事件,但是我一定在事件觸發n秒後才執行,如果你在一個事件觸發的n秒内又觸發了這個事件,那我就以新的事件的時間為準,n秒後才執行,總之,就是要等到你觸發完事件n秒内不再觸發事件。

最簡陋的函數防抖方式

//例如給添加的onmousemove事件
 var count = ;
 var container = document.getElementById("container");
 function moveAction(){
     container.innerHTML = count++;
 }
 //注冊滑鼠移動時間
 container.onmousemove = debounce(moveAction,);
 //函數防抖,外面包一層函數debounce的原因是裡層函數形成閉包,儲存外部函數的變量,也就是上一次的timeoutId,以此友善後面清除。
 function debounce(func,wait){
     var timeout;
     return function(){
         clearTimeout(timeout);
         timeout = setTimeout(func,wait);
     }
 }
           

不使用debounce的時候,注冊的事件處理程式的this指向的是id為container的dom節點,但是使用了debounce函數以後this值指向了window,是由于由

setTimeout()

調用的代碼運作在與所在函數完全分離的執行環境上。 詳細可參考MDN setTimeout 這會導緻,這些代碼中包含的

this

關鍵字在非嚴格模式會指向

window

(或全局)對象,嚴格模式下為 undefined,這和所期望的

this

的值是不一樣的。 為了保證一緻性,對debounce函數改寫如下

function debounce(func,wait){
     var timeout;
     return function(){
         var context = this;
         clearTimeout(timeout);
         setTimeout(function(){
             func.apply(context);
         },wait);
     }
 }
           

但是上面的debounce函數還是有問題,就是事件處理函數一般都會傳入事件對象e,通過事件對象擷取一些資料,但是在上面代碼中指定的func裡面擷取事件對象為undefined,因為根本就沒有傳如其它的參數,是以需要傳入參數。改寫如下

function debounce(func,wait){
     var timeout;
     return function(){
         var context = this;
         var args = arguments;//接收到參數
         clearTimeout(timeout);
         setTimeout(function(){
             func.apply(context,args);
         },wait);
     }
 }
           

函數節流(throttle)與函數防抖(debounce)應用場景

函數節流(throttle)

  • 頻繁的mousemove/keydown,比如高頻的滑鼠移動,遊戲射擊類的
  • 搜尋聯想(keyup)
  • 進度條(我們可能不需要高頻的更新進度)
  • 拖拽的dragover等
  • 高頻的點選,抽獎等

    函數防抖(debounce)

  • scroll/resize事件
  • 文本連續輸入,ajax驗證/關鍵字搜尋

更多進階的用法,可以參考下面的連結中!

參考:

JavaScript專題之跟着 underscore 學節流

JavaScript專題之跟着underscore學防抖

繼續閱讀