UnderScore源碼看防抖和節流

龜兔賽跑(快不必定好,慢也不必定差)

相信這事一個你們均可以耳熟能詳的例子了,兔子跑得很快,這是他勝利的優點,可是同時也是"快"讓它有了驕傲的思想,致使本身輕敵兒錯失了勝利的機會。git

烏龜跑得很慢,可是他很謙虛,一步一個腳印,穩定向前抓住機遇取得了勝利!github

瀏覽器裏的"龜兔賽跑"

咱們在瀏覽器中時常會遇到一些高頻率事件:onscroll oninput resize onkeyup keydown... 那麼當遇到這些的時候咱們到底應該讓它像"兔子同樣快速執行",仍是像"烏龜同樣穩步向前"呢? 可是若是快速執行又可能會在較大的項目或者低版本瀏覽器中形成頁面卡頓不流暢影響用戶體驗(如不理解請谷歌搜索瀏覽器渲染過程+重繪、迴流)!而像烏龜同樣穩步向前形成卡頓的概率會小不少,並且對於用戶使用的體驗基本毫無影響!因此咱們仍是穩步向前爲好! 瀏覽器

那麼怎麼作到穩步執行呢?

優化高頻事件 》》下降代碼執行頻率bash

那麼怎麼作到下降代碼執行頻率呢?

下降代碼執行頻率 》》 throttle(節流)|| debounce(防抖)app

throttle(節流)和 debounce(防抖)

請先記住兩個概念:節流>間隔固定時間執行,防抖>你不中止我就不執行。請您記住,記住,記住!!! 不要再分不清這兩個詞語哪一個是哪一個了!!!!函數

throttle(節流)

直接上代碼,我們先看一個簡單版本容易理解的。優化

function throttle(fn, threshhold=150) {
var timeout;//方便清除定時器
var start = new Date;//開始的時間
return function () {
var context = this, args = arguments, curr = new Date() - 0
clearTimeout(timeout)//老是幹掉事件回調(與後面的「讓方法在脫離事件後也能執行一次」對應)
if(curr - start >= threshhold){ 
    fn.apply(context, args) 
    start = curr //其實到這裏咱們就已經實現了節流的邏輯
}else{
   //讓方法在脫離事件後也能執行一次(好比咱們最後一次點擊一個按鈕,點擊後按照上面的邏輯當小於threshhold是不會執行此次點擊事件的回調函數的,因此加上這個定時器確保最後一次不管間隔時間多大均可以執行)
    timeout = setTimeout(function(){
       fn.apply(context, args) 
    }, threshhold);
   }
 }
}
var logger = throttle(function(e) {
console.log(e.pageX, e.pageY)
});

// 綁定監聽
document.querySelector("#btn").addEventListener('click',logger);
複製代碼

下面看看UnderScore對throttle(節流)的封裝ui

function throttle(func, wait, options) {;
      let args, context, previous = 0, timeout;
      let later = function () {//最後一次定時器中的回調
        func.apply(context, args);
        args = context = null
      }
      let throttled = function () {
        args = arguments;
        context = this;
        let now = Date.now(); // 如今的時間
        let remaning = wait - (now - previous);
        if (remaning <= 0) {
          if (timeout) {//屢次連續點擊的時候就清除定時器,由於它不是最後一次點擊,只有最後一次點擊後纔會保留定時器
            clearTimeout(timeout);
            timeout = null;
          }
          func.apply(context, args);
          previous = now;
        } else if (!timeout && options.trailing !== false) {//若是咱們不傳trailing那麼就是undefined一樣不等於flase
        //先判斷是否存在timeout,存在就不增長定時器,避免屢次定義定時器
          timeout = setTimeout(later, remaning);
        }
      }
      return throttled;
    }
    function logger() {
      console.log('logger');
    }
    //參數trailiing:達到讓最後一次事件觸發後回調方法仍是能執行的效果
    btn.addEventListener('click', throttle(logger, 1000, { trailiing: true }));
    
    //參數leading:達到讓第一次事件觸發後不馬上執行回調
    function throttle(func, wait, options) {
      let args, context, previous = 0, timeout;
      let later = function () {
        previous = options.leading === false ? 0 : Date.now();
        //第二步:定時器回調中讓previous迴歸正常
        func.apply(context, args);
        args = context = null
      }
      let throttled = function () {
        args = arguments;
        context = this;
        let now = Date.now();
        if (!previous && options.leading === false) previous = now;
        //第一步:使remaning必定大於0以此來達到讓它走 else if,也就是定義定時器延遲處理事件。
        let remaning = wait - (now - previous);
        if (remaning <= 0) {
          if (timeout) {
            clearTimeout(timeout);
            timeout = null;
          }
          func.apply(context, args);
          previous = now;
        } else if (!timeout && options.trailing !== false) {
          timeout = setTimeout(later, remaning);
        }
      }
      return throttled;
    }
    function logger() {
      console.log('logger');
    }
    // btn.addEventListener('click', throttle(logger, 1000, { trailiing: true }));
    // 延遲第一次點擊 是不生效的
    btn.addEventListener('click', throttle(logger, 1000, { leading: false }));
複製代碼

此處是本身對UnderScore中throttle源碼的簡單重寫。若有不懂的請私信我。。this

debounce(防抖)

防抖實現十分簡單spa

function debounce(func,wait) {
      let timeout;
      return function () {
        clearTimeout(timeout); //有定時器就先清掉,始終保證只有一個定時器
        timeout = setTimeout(() => {
          func.apply(this,arguments);
          timeout = null;
        }, wait);
      }
    }
    function logger(e) {
      console.log('logger');
    }
    btn.addEventListener('click', debounce(logger,1000));
複製代碼

可是呢,這樣又有點bug,那就是咱們點擊第一次也要等好久的定時器時間間隔才能夠看到效果,而咱們有時會但願的是點擊了立刻就有效果。

下面看看UnderScore對 debounce(防抖)的封裝

function debounce(func,wait,immediate) {
      let timeout;
      return function () {
        clearTimeout(timeout);
        if(immediate){
          let callNow = !timeout;  //第一次點擊的話timeout就是undefined取反就是true,就會執行下一行,第二次點擊的話就timeout不爲空就不會按照原來的邏輯執行了。這樣也就達到了點擊第一次當即執行的效果。
          if(callNow) func.apply(this, arguments);
        }
        timeout = setTimeout(() => {
          func.apply(this,arguments);
          timeout = null;
        }, wait);
      }
    }
    function logger(e) {
      console.log('logger',e);
    }
    // 第三個參數 表示首次 先觸發一下
    btn.addEventListener('click', debounce(logger,1000,true));
複製代碼

上面就是UnderScore對節流、防抖的基本實現了,固然還有一個取消的方法,可是那個很簡單能夠自行去看一看

連接:github.com/jashkenas/u…

文章未完待續...

ToDo:(1)將文章思路再理一遍,配些動圖和例子一步步的實現一下防抖節流

ToDo:(2)將lodash的防抖節流一步步實現一遍

PS:過年在家長膘,寫得有點籠統 還望大佬們海涵

相關文章
相關標籤/搜索