函式節流與函式防抖

重科前端小鑫發表於2020-07-21

函式節流(throttle)

函式節流:在指定的間隔時間內只執行一次
有個需要頻繁觸發函式,出於優化效能角度,在規定時間內,只讓函式觸發的第一次生效,後面不生效。
比如下面的例子,在不加函式節流的時候,每當滾動條滾動的時候都會觸發一次,造成大量的效能浪費

// 未新增節流函式
document.onscroll = function () {
  console.log('scroll事件被觸發了')
}

新增了節流函式後

// 新增了節流函式
document.onscroll = throttle(function () {
  console.log('scroll事件被觸發了')
}, 300)


具體程式碼實現

/**
 * @description 函式節流
 * @param {Function} fn 需要執行函式節流的函式
 * @param {Number} interval 指定間隔時間
 */
function throttle(fn, interval = 300) {
  let canRun = true // 通過閉包儲存一個標記
  return function () {
    if (!canRun) return // 第一次呼叫執行
    canRun = false // setTimeout未執行時,後續fn函式呼叫都不會再執行
    // setTimeout 定時器延時執行
    setTimeout(() => {
      fn.apply(this, arguments)
      canRun = true // 標記為true,節流完成
    }, interval)
  }
}

程式碼解釋
簡單來說,函式的節流就是通過閉包儲存一個標記(canRun = true), 在函式的開頭判斷這個標記是否為true,如果這個標記為true的話就繼續執行,否則就return掉,判斷完標記後立即把這個標記設定為false,然後把外部傳入的函式的執行包在一個setTimout中,最後在定時器執行完畢之後再把標記設定為true,表示本次延遲執行完畢,可以執行下一次迴圈了。當定時器還未執行完畢的時候,canRun這個標記始終未false,故在開頭的判斷中總是被return掉,函式並未執行。、
應用場景
監聽滾動事件判斷是否到頁面底部自動載入更多:給 scroll 加了 debounce 後,只有使用者停止滾動後,才會判斷是否到了頁面底部;如果是 throttle 的話,只要頁面滾動就會間隔一段時間判斷一次等

函式防抖(debounce)

函式防抖: 一個需要頻繁觸發的函式,在規定時間內,只讓最後一次生效,前面的不生效。
比如點選一個按鈕,每點選一次就會觸發一次事件,在沒有加防抖函式的情況下,快速點選會導致多次觸發

// 未加防抖函式
document.getElementById('btn').onclick = function(){
  console.log('我被點選了');
}

在加了防抖函式後,只會在規定時間後觸發一次

// 加了防抖函式
document.getElementById('btn').onclick = debounce(function(){
  console.log('我被點選了');
},300)

具體程式碼實現

/**
 * @description 函式防抖
 * @param {Function} fn 需要執行函式防抖的函式
 * @param {Number} interval 指定間隔時間
 */
function debounce(fn, interval = 300) {
  let timeout = null // 通過閉包儲存一個標記
  return function () {
    clearInterval() // 把前一個定時器去掉
    // 又建立一個新的定時器
    timeout = setTimeout(() => {
      fn.apply(this, arguments) // 指定的時間間隔之後執行fn
    }, interval)
  }
}

程式碼解釋
其原理就第一次呼叫函式,建立一個定時器,在指定的時間間隔之後執行程式碼。當第二次呼叫該函式時,它會清除前一次的定時器並設定另一個。如果前一個定時器已經執行過了,這個操作就沒有任何意義。然而,如果前一個定時器尚未執行,其實就是將其替換為一個新的定時器,然後延遲一定時間再執行。
應用場景
文字輸入的驗證(連續輸入文字後傳送 AJAX 請求進行驗證,驗證一次就好)

相關文章