JS函式節流,去抖

舞動乾坤發表於2018-05-23

原文連結: 快速介紹幾個JS函式

JS中函式是一等公民,地位很高。本文介紹快速介紹三個函式:

  • throttle 節流函式
  • debounce 去抖函式
  • compose 組合函式,這函式就是把一系列函式按順序一個個執行。

節流和去抖函式

當某些事件頻繁觸發造成不斷執行DOM操作或資源載入等行為,這時你可能就會想到用節流和去抖函式來處理。

throttle 節流函式

節流函式就是預先設定一個執行週期,當呼叫動作的時刻大於等於執行週期則執行該動作,然後進入下一個新週期。

可以用在下面兩個情景中:

  • window物件的resize、scroll事件
  • 拖拽時的mousemove事件

簡單實現

functionthrottle (fn, delay) {
    let last = 0
    return(...args) => {
        const now = Date.now()
        if (now - last > delay) {
            fn.call(this, args)
            last = now
        }
    }
}
複製程式碼

debounce 去抖函式

去抖函式就是呼叫動作n毫秒後,才會執行該動作,若在這n毫秒內又呼叫此動作則將重新計算執行時間。

通常用在下面的場景中:

  • 文字輸入、自動完成的keyup事件

簡單實現

functiondebounce (fn, delay) {
    let last = 0
    return(...args) => {
        clearTimeout(last)
        last = setTimeout(() => {
            fn.call(this, args)
        }, delay)
    }
}
複製程式碼

這邊找了一個網上的實現版本:

/*
* 頻率控制 返回函式連續呼叫時,fn 執行頻率限定為每多少時間執行一次
* @param fn {function}  需要呼叫的函式
* @param delay  {number}    延遲時間,單位毫秒
* @param immediate  {bool} 給 immediate引數傳遞false 繫結的函式先執行,而不是delay後後執行。
* @return {function}實際呼叫函式
*/var throttle = function (fn,delay, immediate, debounce) {
   var curr = +newDate(),//當前事件
       last_call = 0,
       last_exec = 0,
       timer = null,
       diff, //時間差
       context,//上下文
       args,
       exec = function () {
           last_exec = curr;
           fn.apply(context, args);
       };
   returnfunction () {
       curr= +newDate();
       context = this,
       args = arguments,
       diff = curr - (debounce ? last_call : last_exec) - delay;
       clearTimeout(timer);
       if (debounce) {
           if (immediate) {
               timer = setTimeout(exec, delay);
           } elseif (diff >= 0) {
               exec();
           }
       } else {
           if (diff >= 0) {
               exec();
           } elseif (immediate) {
               timer = setTimeout(exec, -diff);
           }
       }
       last_call = curr;
   }
};
 
/*
* 空閒控制 返回函式連續呼叫時,空閒時間必須大於或等於 delay,fn 才會執行
* @param fn {function}  要呼叫的函式
* @param delay   {number}    空閒時間
* @param immediate  {bool} 給 immediate引數傳遞false 繫結的函式先執行,而不是delay後後執行。
* @return {function}實際呼叫函式
*/var debounce = function (fn, delay, immediate) {
   return throttle(fn, delay, immediate, true);
};
複製程式碼

自己畫了一張圖便於理解,最左邊的boolean值是上邊的immediate值,沿x軸的線是時間線,時間線上面是使用者觸發的動作,下面是函式的執行。

上面函式有點奇怪的地方就是immediate引數傳遞false 繫結的函式先執行。 ![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="796" height="777"></svg>) 先看debounceimmediate不同的情況下的表現:

immediatefalse的情況下,第一次觸發動作,函式就馬上被執行了,後續的在delay時間段內重複按鍵都不會觸發函式,而且會重置delay開始計算的時間點。當觸發動作間隔時間超過delay後,又回到了初始狀態,一觸發動作就執行函式。

immediatetrue的情況下,觸發動作後,函式並沒有馬上執行,只有在經過delay時間段內,沒有觸發動作時,函式才會被執行。

接著看throttleimmediate不同的情況下的表現:

immediatefalse的情況下,第一次觸發動作,函式就馬上被執行了,在這次執行後,在後續的delay時間段內觸發動作都不會執行函式,間隔大於delay後,回到初始狀態,觸發動作函式就執行。

immediatetrue的情況下,第一次觸發動作,函式就馬上被執行了,同時定時器設定延時delay時間後在執行函式。其實和上面情況差不多,只不過會在delay後多執行一次。當然還有一些細節不同,圖中可以看到,有兩個執行之間時間並沒有間隔delay。

compose函式

這個直接上程式碼了,是從redux那邊借過來的。

/**
 * Composes single-argument functions from right to left. The rightmost
 * function can take multiple arguments as it provides the signature for
 * the resulting composite function.
 *
 * @param {...Function} funcs The functions to compose.
 * @returns {Function} A function obtained by composing the argument functions
 * from right to left. For example, compose(f, g, h) is identical to doing
 * (...args) => f(g(h(...args))).
 */exportdefaultfunctioncompose(...funcs) {
  if (funcs.length === 0) {
    returnarg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
複製程式碼

核心程式碼就一行,沒錯,就一行。

沒看到之前,我可能會通過for迴圈來做,上面程式碼用到陣列方法reduce,在配合剩餘引數語法和擴充套件運算子...來傳參就可以實現函式組合的功能,非常精簡。

介紹到這裡就結束了,後續碰到到驚豔的函式還會繼續分享。

相關文章