JavaScript 節流、防抖

爲何須要節流、防抖

當某個事件頻繁觸發時,事件處理函數會頻繁執行,若是處理函數有一些費時、耗性能的操做,就會致使頁面出現卡頓甚至瀏覽器崩潰,這時就須要 節流防抖

什麼是節流、防抖

  • 節流
當事件頻發觸發時,事件處理程序每隔一段時間執行一次
  • 防抖
若是某個頻繁觸發的事件在規定的時間內沒有再次觸發,則執行事件處理程序,若是在這段時間內事件再次觸發了,則從新計時

何時須要節流、防抖

  • scroll
  • resize
  • 表單輸入(input事件)校驗
  • ...

實現

  • 防抖

<!DOCTYPE html>
<html lang = "en">
  <style>
    #wrap .scrollBox, #wrap .list{
      float: left;
    }
    .scrollBox {
      width: 40%;
      height: 500px;
      margin-right: 5%;
      border: 1px solid red;
      overflow: scroll;
    }
    .list {
      width: 40%;
      border: 1px solid red;
    }
  </style>
  <body>
    <div id = "wrap">
      <div class = "scrollBox"><!-- 內容動態生成 --></div>
      <ul class = "list">
        <li>我是列表</li>
      </ul>
    </div>
  </body>
  <script>
    // 防抖函數,不如不理解爲何能防抖,請看下面的解釋內容
    function debounce (handle, delay) {
      let timer = null
      return function () {
        if (timer) {
          // console.log('clearTimeout, clear timer')
          clearTimeout(timer)
        }
        // console.log('setTimeout, set timer')    
        timer = setTimeout(handle, delay)
      }
    }
    // 事件處理函數,生成一個li節點,並添加到ul節點的末尾
    function handle () {
      const node = document.createElement('li')
      const text = document.createTextNode(Date.now())
      node.appendChild(text)
      document.getElementsByClassName('list')[0].appendChild(node)
    }
    // 添加事件監聽器,造成了一個閉包
    document.getElementsByClassName('scrollBox')[0].addEventListener('scroll', debounce(handle, 500))
    
    // 生成滾動欄的內容,如下部分和上面的html、css只爲構造一個實驗環境
    let content = ''
    for (let i = 0; i < 1000; i++) {
      content += '在我上面滾動鼠標'
    }
    const textNode = document.createTextNode(content)
    document.getElementsByClassName('scrollBox')[0].appendChild(textNode)
  </script>
</html>

debounce函數爲何會有防抖的功能呢?javascript

document.getElementsByClassName('scrollBox')[0].addEventListener('scroll', debounce(handle, 500)),這行代碼造成了一個閉包, debounce函數運行之後返回一個函數,而返回的這個函數在scroll事件觸發期間(即一直滾動頁面)其實一直在不停的執行(能夠放開註釋掉的兩行 console.log()進行測試),好比:第一次開始滾動,第一次執行到 debounce返回的函數,發現 !!timer === falseif條件不成立,直接執行後面的 setTimeout,設置 timer,可是鼠標在一直滾動,立刻 debounce返回的函數又在執行第二次了,發現 timer不爲空, if條件成立,執行 clearTimeout清除 timer,可是這時你會發現剛纔設置的定時任務尚未執行,就被清除了,這就達到了防抖的要求,鼠標一直滾動一直重複上面的過程(函數一直在執行),直到中止滾動,中止前一次設置的定時任務不會被清除,規定的延時時間一到,執行 handle(處理程序),下一次開始滾動,清除上一次的timer,接着重複上面的過程
  • 節流

有了防抖的基礎,節流這裏,就省略掉構建實驗環境的html代碼了,直接上節流方法的實現,有兩種實現方式,分別是時間戳和定時任務

定時任務css

// 節流函數
function throttle (handle, delay) {
  // 節流的關鍵點在於timer,timer被賦值之後,只有在定時任務執行之後,timer纔會從新被置爲null
  let timer = null
  return function () {
    if (!timer) {
      // 設置timer
      timer = setTimeout(() => {
        handle()
        // 定時任務執行完畢,置空timer
        timer = null
      }, delay)
    }    
  }
}
// 事件處理函數
function handle () {
  console.log('I am handle function')
}
// 添加事件監聽器
document.getElementById('app').addEventListener('scroll', throttle(handle, 1000))

時間戳html

// 節流函數
function throttle (handle, delay) {
  // 節流的關鍵所在
  let prevTime = Date.now()
  return function () {
    if (Date.now() - prevTime >= delay) {
      // 時間到了之後執行事件處理程序,並重置prevTime
      handle()
      prevTime = Date.now()
    }    
  }
}
// 事件處理函數
function handle () {
  console.log('I am handle function')
}
// 添加事件監聽器
document.getElementById('app').addEventListener('scroll', throttle(handle, 1000))

節流和防抖有什麼區別

  • 共同點
都是限制事件處理程序的執行頻率
  • 區別
節流: 定時重複執行事件處理程序,不論事件觸發有多頻繁
防抖:只有事件最後一次觸發(指定時間內沒有再次觸發)纔會執行處理程序

應用場景

  • 防抖

  1. search搜索框、表單輸入驗證等觸發input事件的操做
只有內容輸入完成之後,才執行最後的事件處理程序(好比:搜索內容、驗證表單內容)
  1. 窗口的resize事件
窗口resize中止後,最後執行一次事件處理程序
  • 節流

  1. 鼠標頻繁點擊的場景
遊戲,好比打地鼠、CS、英雄聯盟、王者榮耀等須要頻繁點擊操做的場景
  1. 下拉刷新
下拉加載更多,一直下拉,但一段事件內只執行一次事件處理程序(加載內容)
相關文章
相關標籤/搜索