JS 函數節流和去抖


1、什么是節流和去抖?

  節流。就是擰緊水龍頭讓水少流一點,但是不是不讓水流了。想象一下在現實生活中有時候我們需要接一桶水,接水的同時不想一直站在那等着,可能要離開一會去干一點別的事請,讓水差不多流滿一桶水的時候再回來,這個時候,不能把水龍頭開的太大,不然還沒回來水就已經滿了,浪費了好多水,這時候就需要節流,讓自己回來的時候水差不多滿了。那在JS里有沒有這種情況呢,典型的場景是圖片懶加載監聽頁面的scoll事件,或者監聽鼠標的mousemove事件,這些事件對應的處理方法相當於水,由於scroll和mousemove在鼠標移動的時候會被瀏覽器頻繁的觸發,會導致對應的事件也會被頻繁的觸發(水流的太快了),這樣就會造成很大的瀏覽器資源開銷,而且好多中間的處理是不必要的,這樣就會造成瀏覽器卡頓的現象,這時候就需要節流,如何節流呢?我們無法做到讓瀏覽器不去觸發對應的事件,但是可以做到讓處理事件的方法執行頻率減少,從而減少對應的處理開銷。

  去抖。最早接觸這個詞應該是在高中物理里面學到的,有時候開關在在真正閉合之前可能會發生一些抖動現象,如果抖動的明顯的話,對應的小燈泡可能會閃爍,把燈泡閃壞了不重要,萬一把眼睛再給閃壞了可就麻煩了,這個時候就有去抖電路的出現。而在我們的頁面里,也有這種情況,假設我們的一個輸入框,輸入內容的同時可能會去后台查詢對應的聯想 詞,如果用戶輸入的同時,頻繁的觸發input事件,然后頻繁的向后抬發送請,那么直到用戶輸入完成時,之前的請求都應該是多余的,假設網絡慢一點,后台返回的數據比較慢,那么顯示的聯想詞可能會出現頻繁的變換,直到最后的一個請求返回。這個時候就可以在一定時間內監聽是否再次輸入,如果沒有再次輸入則認為本次輸入完成,發送請求,否則就是判定用戶仍在輸入,不發送請求。

  去抖和節流是不同的,因為節流雖然中間的處理函數被限制了,但是只是減少了頻率,而去抖則把中間的處理函數全部過濾掉了,只執行規判定時間內的最后一個事件。

2、JS實現。

  前面BB了這么多,感謝你耐心的看到這里,接下來我們來自己動手看看如何實現節流和去抖。

  節流: 

    /** 實現思路:
    **  參數需要一個執行的頻率,和一個對應的處理函數,
    **  內部需要一個lastTime 變量記錄上一次執行的時間
    **/ function throttle (func, wait) {
      let lastTime = null
    // 為了避免每次調用lastTime都被清空,利用js的閉包返回一個function確保不生命全局變量也可以 return function () { let now = new Date() // 如果上次執行的時間和這次觸發的時間大於一個執行周期,則執行 if (now - lastTime - wait > 0) { func() lastTime = now } } }

再看如何調用:

// 由於閉包的存在,調用會不一樣
let throttleRun = throttle(() => {
    console.log(123)
}, 400)
window.addEventListener('scroll', throttleRun)

 這時候f瘋狂的滾動頁面,會發現會400ms打印一個123,而沒有節流的話會不斷地打印, 你可以改變wait參數去感受下不同。

但是到這里,我們的節流方法是不完善的,因為我們的方法沒有獲取事件發生時的this對象,而且由於我們的方法簡單粗暴的通過判斷這次觸發的時間和上次執行時間的間隔來決定是否執行回調,這樣就會造成最后一次觸發無法執行,或者用戶出發的間隔確實很短,也無法執行,造成了誤殺,所以需要對方法進行完善。

  function throttle (func, wait) {
      let lastTime = null
      let timeout
      return function () {
        let context = this
        let now = new Date()
        // 如果上次執行的時間和這次觸發的時間大於一個執行周期,則執行
        if (now - lastTime - wait > 0) {
          // 如果之前有了定時任務則清除
          if (timeout) {
            clearTimeout(timeout)
            timeout = null
          }
          func.apply(context, arguments)
          lastTime = now
        } else if (!timeout) {
          timeout = setTimeout(() => {
            // 改變執行上下文環境
            func.apply(context, arguments)
          }, wait)
        }
      }
    }

這樣我們的方法就相對完善了,調用方法和之前相同。

    去抖:

  去抖的方法,和節流思路一致,但是只有在抖動被判定結束后,方法才會得到執行。

   debounce (func, wait) {
      let lastTime = null
      let timeout
      return function () {
        let context = this
        let now = new Date()
        // 判定不是一次抖動
        if (now - lastTime - wait > 0) {
          setTimeout(() => {
            func.apply(context, arguments)
          }, wait)
        } else {
          if (timeout) {
            clearTimeout(timeout)
            timeout = null
          }
          timeout = setTimeout(() => {
            func.apply(context, arguments)
          }, wait)
        }
        // 注意這里lastTime是上次的觸發時間
        lastTime = now
      }
    }

  這時候按照之前同樣的方式調用,會發現無論怎么瘋狂的滾動窗口,只有停止滾動時,才會執行對應的事件。

  去抖和節流已經有很多成熟的js進行了實現,其大致思路基本是這樣的。

 

注:本文出自博客園 https://home.cnblogs.com/u/mdengcc/ ,轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM