輕鬆理解JS函數節流和函數防抖

JS函數防抖和函數節流

問題引入

問題1: 若是實現了dom拖拽功能,可是在綁定拖拽事件的時候發現每當元素稍微移動一點便觸發了大量的回調函數,致使瀏覽器直接卡死,這個時候怎麼辦?javascript

屏幕快照 2017-12-16 下午7.21.39

**問題2:**若是給一個按鈕綁定了表單提交的post事件,可是用戶有些時候在網絡狀況極差的狀況下屢次點擊按鈕形成表單重複提交,如何防止屢次提交的發生?vue

爲了應對如上場景,便出現了函數防抖函數節流兩個概念,總的來講:java

這兩個方法是在時間軸上控制函數的執行次數。git

函數防抖(debounce)

概念: 在事件被觸發n秒後再執行回調,若是在這n秒內又被觸發,則從新計時。github

生活中的實例: 若是有人進電梯(觸發事件),那電梯將在10秒鐘後出發(執行事件監聽器),這時若是又有人進電梯了(在10秒內再次觸發該事件),咱們又得等10秒再出發(從新計時)。segmentfault

函數節流(throttle)

概念: 規定一個單位時間,在這個單位時間內,只能有一次觸發事件的回調函數執行,若是在同一個單位時間內某事件被觸發屢次,只有一次能生效。瀏覽器

生活中的實例: 咱們知道目前的一種說法是當 1 秒內連續播放 24 張以上的圖片時,在人眼的視覺中就會造成一個連貫的動畫,因此在電影的播放(之前是,如今不知道)中基本是以每秒 24 張的速度播放的,爲何不 100 張或更可能是由於 24 張就能夠知足人類視覺需求的時候,100 張就會顯得很浪費資源。網絡

分析圖

假設,咱們觀察的總時間爲10秒鐘,規定1秒做爲一次事件的最小間隔時間。閉包

若是觸發事件的頻率是 0.5s/次,那麼

函數防抖如圖 app

屏幕快照 2017-12-16 下午7.54.01

由於始終無法等一秒鐘就被再次觸發了,因此最終沒有一次事件是成功的。

函數節流如圖

屏幕快照 2017-12-16 下午7.50.45

由於控制了最多一秒一次,頻率爲0.5s/次,因此每一秒鐘就有一次事件做廢。最終控制成1s/次

若是觸發事件的頻率是 2s/次,那麼

函數防抖如圖

屏幕快照 2017-12-16 下午8.00.29
由於 2s/次已經大於了規定的最小時間,因此每計時兩秒便觸發一次。

函數節流如圖

屏幕快照 2017-12-16 下午7.58.04

一樣,2s/次 大於了最小時間規定,因此每一次觸發都生效。

應用場景

對於函數防抖,有如下幾種應用場景:

  • 給按鈕加函數防抖防止表單屢次提交。
  • 對於輸入框連續輸入進行AJAX驗證時,用函數防抖能有效減小請求次數。
  • 判斷scroll是否滑到底部,滾動事件+函數防抖

總的來講,適合屢次事件一次響應的狀況

對於函數節流,有以下幾個場景:

  • 遊戲中的刷新率
  • DOM元素拖拽
  • Canvas畫筆功能

總的來講,適合大量事件按時間作平均分配觸發。

源碼

函數防抖:

function debounce(fn, wait) {
  var timer = null;
  return function () {
      var context = this
      var args = arguments
      if (timer) {
          clearTimeout(timer);
          timer = null;
      }
      timer = setTimeout(function () {
          fn.apply(context, args)
      }, wait)
  }
}

var fn = function () {
  console.log('boom')
}

setInterval(debounce(fn,500),1000) // 第一次在1500ms後觸發,以後每1000ms觸發一次

setInterval(debounce(fn,2000),1000) // 不會觸發一次(我把函數防抖看出技能讀條,若是讀條沒完成就用技能,便會失敗並且從新讀條)
複製代碼

之因此返回一個函數,由於防抖自己更像是一個函數修飾,因此就作了一次函數柯里化。裏面也用到了閉包,閉包的變量是timer

函數節流

function throttle(fn, gapTime) {
  let _lastTime = null;

  return function () {
    let _nowTime = + new Date()
    if (_nowTime - _lastTime > gapTime || !_lastTime) {
      fn();
      _lastTime = _nowTime
    }
  }
}

let fn = ()=>{
  console.log('boom')
}

setInterval(throttle(fn,1000),10)
複製代碼

如圖是實現的一個簡單的函數節流,結果是一秒打出一次boom

小結

函數防抖和函數節流是在時間軸上控制函數的執行次數。防抖能夠類比爲電梯不斷上乘客,節流能夠看作幻燈片限制頻率播放電影

擴展閱讀

Github: JavaScript 函數節流和函數去抖應用場景辨析

SegmentFault:函數節流與函數防抖

Github:函數節流與函數防抖

相關文章
相關標籤/搜索