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學防抖