1. 程式人生 > >函式節流和去抖動

函式節流和去抖動

使用場景

以下場景往往由於事件頻繁被觸發,因而頻繁執行DOM操作、資源載入等重行為,導致UI停頓甚至瀏覽器崩潰。

  • window物件的resize、scroll事件
  • 拖拽時的mousemove事件
  • 射擊遊戲中的mousedown、keydown事件
  • 文字輸入、自動完成的keyup事件

實際上對於window的resize事件,實際需求大多為停止改變大小n毫秒後執行後續處理;而其他事件大多的需求是以一定的0執行後續處理。針對這兩種需求就出現了debounce和throttle兩種解決辦法。

debounce

抖動:如果用手指一直按住一個彈簧,它將不會彈起直到你鬆手為止。 也就是說當呼叫動作n毫秒後,才會執行該動作,若在這n毫秒內又呼叫此動作則將重新計算執行時間。
介面定義:

/**
* 空閒控制 返回函式連續呼叫時,空閒時間必須大於或等於 idle,action 才會執行
* @param idle   {number}    空閒時間,單位毫秒
* @param action {function}  請求關聯函式,實際應用需要呼叫的函式
* @return {function}    返回客戶呼叫函式
*/
debounce(idle,action)

簡單實現1:
利用定時器,讓函式執行延遲500毫秒,在500毫秒內如果有函式又被呼叫則刪除上一次呼叫,這次呼叫500毫秒後執行,如此往復

window.onscroll = function(){//lazyload();
debounce(lazyload,window);
};
function debounce(method,context){
clearTimeout(method.timeout);
method.timeout = setTimeout(function(){
method.call(context);
},500);
}
function lazyload(){
console.log("scroll執行了"+scrollnum);
}

簡單實現2:

還有一種節流方式,是通過返回閉包的形式,可以設定延遲時間,兩者執行的結果是一樣,但是我在實際操作的時候設定延遲500時,滾動過了一會才執行了,設定為delay為100的時候在視覺上就沒有感覺延遲。而且函式也只滾動了一次。

function debounce1(method,delay){
    var timer = null;
    return function(){
        var context = this,args = arguments;
        clearTimeout(timer);
        timer = setTimeout(function(){
        method.apply(context,args);
        },delay);
    }
}

throttle

當我一直滾動滑鼠的時候,lazyload函式就會不斷被延遲,這樣只有停下來的時候才會執行,那麼再有些需要及時顯示的情況下,就顯得不那麼友好了(對於實現keyup事件的提示也沒有意義了),所以可以為函式新增一個引數作為到固定間隔必須執行,到了這個時間間隔就必須執行,這個時候就引入了節流:

節流:如果將水龍頭擰緊直到水是以水滴的形式流出,那你會發現每隔一段時間,就會有一滴水流出。也就是會說預先設定一個執行週期,當呼叫動作的時刻大於等於執行週期則執行該動作,然後進入下一個新週期
介面定義:

/**
* 頻率控制 返回函式連續呼叫時,action 執行頻率限定為 次 / delay
* @param delay  {number}    延遲時間,單位毫秒
* @param action {function}  請求關聯函式,實際應用需要呼叫的函式
* @return {function}    返回客戶呼叫函式
*/
throttle(delay,action)

簡單實現:

var throttle = function(delay, action){
  var last = 0;
  return function(){
    var curr = +new Date();
    if (curr - last > delay){
      action.apply(this, arguments);
      last = curr ;
    }
  }
}

簡單實現2:

function throttle2(method, delay, time) {
    var timeout,startTime = new Date();
    return function() {
        var context = this,
        args = arguments,
        curTime = new Date();
        clearTimeout(timeout);// 如果達到了規定的觸發時間間隔,觸發 handler
        if (curTime - startTime >= time) {
            method.apply(context, args);
            startTime = curTime;//沒達到觸發間隔,重新設定定時器
        } else {
            timeout = setTimeout(method, delay);
        }
};