成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

函數(shù)防抖(debounce)和節(jié)流(throttle)以及l(fā)odash的debounce源碼賞析

Enlightenment / 1960人閱讀

摘要:防抖函數(shù)防抖和節(jié)流是一對常常被放在一起的場景。同時,這里會設(shè)置一個定時器,在等待后會執(zhí)行,的主要作用就是觸發(fā)。最后,如果不再有函數(shù)調(diào)用,就會在定時器結(jié)束時執(zhí)行。

函數(shù)節(jié)流和去抖的出現(xiàn)場景,一般都伴隨著客戶端 DOM 的事件監(jiān)聽。比如scroll resize等事件,這些事件在某些場景觸發(fā)非常頻繁。
比如,實現(xiàn)一個原生的拖拽功能(不能用 H5 Drag&Drop API),需要一路監(jiān)聽 mousemove 事件,在回調(diào)中獲取元素當(dāng)前位置,然后重置 dom 的位置(樣式改變)。如果我們不加以控制,每移動一定像素而觸發(fā)的回調(diào)數(shù)量是會非常驚人的,回調(diào)中又伴隨著 DOM 操作,繼而引發(fā)瀏覽器的重排與重繪,性能差的瀏覽器可能就會直接假死,這樣的用戶體驗是非常糟糕的。
我們需要做的是降低觸發(fā)回調(diào)的頻率,比如讓它 500ms 觸發(fā)一次,或者 200ms,甚至 100ms,這個閾值不能太大,太大了拖拽就會失真,也不能太小,太小了低版本瀏覽器可能就會假死,這樣的解決方案就是函數(shù)節(jié)流,英文名字叫「throttle」。

節(jié)流(throttle)

函數(shù)節(jié)流的核心是,讓一個函數(shù)不要執(zhí)行得太頻繁,減少一些過快的調(diào)用來節(jié)流。也就是在一段固定的時間內(nèi)只觸發(fā)一次回調(diào)函數(shù),即便在這段時間內(nèi)某個事件多次被觸發(fā)也只觸發(fā)回調(diào)一次。

防抖(debounce)

函數(shù)防抖(debounce)和節(jié)流是一對常常被放在一起的場景。防抖的原理是在事件被觸發(fā)n秒后再執(zhí)行回調(diào),如果在這n秒內(nèi)又被觸發(fā),則重新計時。也就是說事件來了,先setTimeout定個時,n秒后再去觸發(fā)回調(diào)函數(shù)。它和節(jié)流的不同在于如果某段時間內(nèi)事件以間隔小于n秒的頻率執(zhí)行,那么這段時間回調(diào)只會觸發(fā)一次。節(jié)流則是按照200ms或者300ms定時觸發(fā),而不僅僅是一次。

兩者應(yīng)用場景

初看覺得兩個概念好像差不多啊,到底什么時候用節(jié)流什么時候用防抖呢?

防抖常用場景

防抖的應(yīng)用場景是連續(xù)的事件響應(yīng)我們只觸發(fā)一次回調(diào),比如下面的場景:

resize/scroll 觸發(fā)統(tǒng)計事件

文本輸入驗證,不用用戶輸一個文字調(diào)用一次ajax請求,隨著用戶的輸入驗證一次就可以

節(jié)流常用場景

節(jié)流是個很公平的函數(shù),隔一段時間就來觸發(fā)回調(diào),比如下面的場景:

DOM 元素的拖拽功能實現(xiàn)(mousemove)

計算鼠標(biāo)移動的距離(mousemove)

搜索聯(lián)想(keyup)

為什么這些適合節(jié)流而不是防抖呢?
我們想想哈,按照防抖的概念如果n秒內(nèi)用戶連續(xù)不斷觸發(fā)事件,則防抖會在用戶結(jié)束操作結(jié)束后觸發(fā)回調(diào)。 那對于拖動來說,我拖了半天沒啥反應(yīng),一松手等n秒,啪。元素蹦過來了,這還是拖動嗎?這是跳動吧,2333;

lodash源碼實現(xiàn) 基本節(jié)流實現(xiàn)
function throttle(func, gapTime){
    if(typeof func !== "function") {
        throw new TypeError("need a function");
    }
    gapTime = +gapTime || 0;
    let lastTime = 0;
    
    return function() {
        let time = + new Date();
        if(time - lastTime > gapTime || !lastTime) {
            func();
            lastTime = time;
        }
    }
}

setInterval(throttle(() => {
    console.log("xxx")
}, 1000),10)

如上,沒10ms觸發(fā)一次,但事實上是每1s打印一次 "xxx";

基本防抖實現(xiàn)

弄清防抖的原理后,我們先來實現(xiàn)一個簡單的 debounce 函數(shù)。

// 我的debounce 實現(xiàn)
function my_debounce(func, wait) {

    if(typeof func !== "function") {
        throw new TypeError("need a function");
    }
    wait = +wait || 0;

    let timeId = null;

    return function() {
        // console.log("滾動了滾動了");  // 測試時可放開
        const self = this;
        const args = arguments;

        if(timeId) {
            clearTimeout(timeId);   // 清除定時器,重新設(shè)定一個新的定時器
        }
        timeId =  setTimeout(() => {
            func.apply(self, args); // arguments 是傳給函數(shù)的參數(shù),這里是 event  對象

        }, wait);

    }

}

我們來分析一下這個函數(shù), 首先它是一個閉包。它的核心是 定時器的設(shè)置,如果第一次進(jìn)來, timeId 不存在直接設(shè)置一個延遲 wait 毫秒的定時器; 如果timeId 已經(jīng)存在則先清除定時器再 重新設(shè)置延遲。
如上所說,如果在延遲時間內(nèi) 來了一個事件,則從這個事件到來的時候開始定時。
用該防抖函數(shù)試一下對scroll去抖效果。我在 防抖函數(shù)中放開日志 console.log("滾動了滾動了");, 然后
對滾動添加事件響應(yīng).

function onScroll_1() {
   console.log("執(zhí)行滾動處理函數(shù)啦");  
}
window.addEventListener("scroll", my_debounce(onScroll_1, 1000));

打開頁面,不斷滾動可以在控制臺看到如下圖的console.

從圖中可以看出,我觸發(fā)了90次滾動響應(yīng),但實際上 滾動處理函數(shù)執(zhí)行了一次。
嗯,對上面簡單的例子我們分析下不同情況下,4秒時間內(nèi)防抖調(diào)用的時機和次數(shù).

每隔1.5秒滾動一次,4秒內(nèi)等待1秒觸發(fā)的情況下,會調(diào)用響應(yīng)函數(shù) 2次

每隔 0.5 秒滾動一次,4秒內(nèi)等待1秒觸發(fā)的情況下,一次也不會調(diào)用。

下圖展示了這兩種情況下定時器設(shè)置和函數(shù)調(diào)用情況(費死個猴勁畫的,湊合看不清楚的可以留言)

從上面的分析來看,這個單純的 防抖函數(shù)還是有個硬傷的,是什么呢?
那就是每次觸發(fā)定時器就重新來,每次都重新來,如果某段時間用戶一直一直觸發(fā),防抖函數(shù)一直重新設(shè)置定時器,就是不執(zhí)行,頻繁的延遲會導(dǎo)致用戶遲遲得不到響應(yīng),用戶同樣會產(chǎn)生“這個頁面卡死了”的觀感。
既然如此,那我們是不是可以設(shè)置一個最常等待時間,超過這個事件不管還有沒有事件在觸發(fā),就去執(zhí)行函數(shù)呢?或者我可不可以設(shè)置第一次觸發(fā)的時候立即執(zhí)行函數(shù),再次觸發(fā)的時候再去防抖,也就是說不管如何先 響應(yīng)一次,告訴那些 心急的 用戶我響應(yīng)你啦,我是正常的,接下來慢慢來哦~
答案是,都是可以的。這些屬于更自由的配置,加上這些, debounce 就是一個成熟的防抖函數(shù)了。嗯,是噠~成熟的

既然說到成熟,咱們還是來看下大名鼎鼎的==lodash==庫是怎么將 debounce 成熟的吧!

loadsh中debounce源碼解讀

為了方便,我們忽略lodash 開始對function的注釋完里整版在這 。成熟的 debounce 也才 100多行而已,小場面~~

先來看下完整函數(shù),里面加上了我自己的理解,然后再詳細(xì)分析

function debounce(func, wait, options) {
  let lastArgs,     // debounced 被調(diào)用后被賦值,表示至少調(diào)用 debounced一次
    lastThis,   // 保存 this
    maxWait,     // 最大等待時間
    result,      // return 的結(jié)果,可能一直為 undefined,沒看到特別的作用
    timerId,    // 定時器句柄
    lastCallTime    // 上一次調(diào)用 debounced 的時間,按上面例子可以理解為 上一次觸發(fā) scroll 的時間

  let lastInvokeTime = 0  // 上一次執(zhí)行 func 的時間,按上面例子可以理解為 上次 執(zhí)行 時的時間
  let leading = false     // 是否第一次觸發(fā)時立即執(zhí)行
  let maxing = false     // 是否有最長等待時間
  let trailing = true    // 是否在等待周期結(jié)束后執(zhí)行用戶傳入的函數(shù)

  // window.requestAnimationFrame() 方法告訴瀏覽器您希望執(zhí)行動畫并請求瀏覽器在下一次重繪之前調(diào)用指定的函數(shù)來更新動畫。該方法使用一個回調(diào)函數(shù)作為參數(shù),這個回調(diào)函數(shù)會在瀏覽器重繪之前調(diào)用。
  // 下面的代碼我先注釋,可以先不關(guān)注~意思是沒傳 wait 時 會在某個時候 調(diào)用 window.requestAnimationFrame()
  
  //  以上代碼被我注釋,可以先不關(guān)注

 // 這個很好理解,如果傳入的 func 不是函數(shù),拋出錯誤,老子干不了這樣的活
  if (typeof func != "function") {
    throw new TypeError("Expected a function")
  }
  
  wait = +wait || 0
  if (isObject(options)) {
    leading = !!options.leading
    maxing = "maxWait" in options
    maxWait = maxing ? Math.max(+options.maxWait || 0, wait) : maxWait
    trailing = "trailing" in options ? !!options.trailing : trailing
  }

//  執(zhí)行 用戶傳入的 func
//  重置 lastArgs,lastThis
//  lastInvokeTime 在此時被賦值,記錄上一次調(diào)用 func的時間
  function invokeFunc(time) {
    const args = lastArgs
    const thisArg = lastThis

    lastArgs = lastThis = undefined
    lastInvokeTime = time
    result = func.apply(thisArg, args)
    return result
  }

//  setTimeout 一個定時器
  function startTimer(pendingFunc, wait) {
  // 先不關(guān)注這個
    //if (useRAF) {
      //return root.requestAnimationFrame(pendingFunc)
    //}
    return setTimeout(pendingFunc, wait)
  }

//  清除定時器
  function cancelTimer(id) {
    // 先不關(guān)注
    //if (useRAF) {
      //return root.cancelAnimationFrame(id)
    //}
    clearTimeout(id)
  }

//  防抖開始時執(zhí)行的操作
//  lastInvokeTime 在此時被賦值,記錄上一次調(diào)用 func的時間
//  設(shè)置了立即執(zhí)行func,則執(zhí)行func, 否則設(shè)置定時器
  function leadingEdge(time) {
    // Reset any `maxWait` timer.
    lastInvokeTime = time
    // Start the timer for the trailing edge.
    timerId = startTimer(timerExpired, wait)
    // Invoke the leading edge.
    return leading ? invokeFunc(time) : result
  }

//  計算還需要等待多久
//  沒設(shè)置最大等待時間,結(jié)果為 wait - (當(dāng)前時間 - 上一次觸發(fā)(scroll) )  時間,也就是  wait - 已經(jīng)等候時間
//  設(shè)置了最長等待時間,結(jié)果為 最長等待時間 和 按照wait 計算還需要等待時間 的最小值
  function remainingWait(time) {
    const timeSinceLastCall = time - lastCallTime
    const timeSinceLastInvoke = time - lastInvokeTime
    const timeWaiting = wait - timeSinceLastCall

    return maxing
      ? Math.min(timeWaiting, maxWait - timeSinceLastInvoke)
      : timeWaiting
  }

// 此時是否應(yīng)該設(shè)置定時器/執(zhí)行用戶傳入的函數(shù),有四種情況應(yīng)該執(zhí)行
// 1, 第一次觸發(fā)(scroll)
// 2. 距離上次觸發(fā)超過 wait, 參考上面例子中 1.5 秒觸發(fā)一次,在3s觸發(fā)的情況
// 3.當(dāng)前時間小于 上次觸發(fā)時間,大概是系統(tǒng)時間被人為往后撥了,本來2018年,系統(tǒng)時間變?yōu)?2017年了,嘎嘎嘎
// 4. 設(shè)置了最長等待時間,并且等待時長不小于 最長等待時間了~ 參考上面例子,如果maxWait 為2s, 則在 2s scroll 時會執(zhí)行
  function shouldInvoke(time) {
    const timeSinceLastCall = time - lastCallTime
    const timeSinceLastInvoke = time - lastInvokeTime

    return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
      (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait))
  }

// 執(zhí)行函數(shù)呢 還是繼續(xù)設(shè)置定時器呢? 防抖的核心
// 時間滿足條件,執(zhí)行
// 否則 重新設(shè)置定時器
  function timerExpired() {
    const time = Date.now()
    if (shouldInvoke(time)) {
      return trailingEdge(time)
    }
    // Restart the timer.
    timerId = startTimer(timerExpired, remainingWait(time))
  }

// 執(zhí)行用戶傳入的 func 之前的最后一道屏障  func os: 執(zhí)行我一次能咋地,這么多屏障?
// 重置 定時器
// 執(zhí)行 func
// 重置 lastArgs = lastThis 為 undefined
  function trailingEdge(time) {
    timerId = undefined

    // Only invoke if we have `lastArgs` which means `func` has been
    // debounced at least once.
    if (trailing && lastArgs) {
      return invokeFunc(time)
    }
    lastArgs = lastThis = undefined
    return result
  }

// 取消防抖
//  重置所有變量  清除定時器
  function cancel() {
    if (timerId !== undefined) {
      cancelTimer(timerId)
    }
    lastInvokeTime = 0
    lastArgs = lastCallTime = lastThis = timerId = undefined
  }

// 定時器已存在,去執(zhí)行 嗯,我就是這么強勢
  function flush() {
    return timerId === undefined ? result : trailingEdge(Date.now())
  }

//  是否正在 等待中
  function pending() {
    return timerId !== undefined
  }

//  正房來了! 這是入口函數(shù),在這里運籌帷幄,根據(jù)敵情調(diào)配各個函數(shù),勢必騙過用戶那個傻子,我沒有一直在執(zhí)行但你以為我一直在響應(yīng)你哦 
  function debounced(...args) {
    const time = Date.now()
    const isInvoking = shouldInvoke(time)

    lastArgs = args
    lastThis = this
    lastCallTime = time

    if (isInvoking) {
      if (timerId === undefined) {
        return leadingEdge(lastCallTime)
      }
      if (maxing) {
        // Handle invocations in a tight loop.
        timerId = startTimer(timerExpired, wait)
        return invokeFunc(lastCallTime)
      }
    }
    if (timerId === undefined) {
      timerId = startTimer(timerExpired, wait)
    }
    return result
  }
  
  debounced.cancel = cancel
  debounced.flush = flush
  debounced.pending = pending
  //  下面這句話證明 debounced 我是入口函數(shù),是正宮娘娘!
  return debounced
}

export default debounce

第一看是不是有點暈?沒關(guān)系,我們結(jié)合例子理一遍 這個成熟的 debounce 是如何運作的。

用demo 理解 loadsh debounce

調(diào)用如下:

function onScroll_1() {
   console.log("執(zhí)行滾動處理函數(shù)啦");  
}
window.addEventListener("scroll", debounce(onScroll_1, 1000));

每 1500 ms 觸發(fā)(scroll)一次

每 600 ms 觸發(fā)(scroll)一次

再來看一下入口函數(shù) debounced。

function debounced(...args) {
    const time = Date.now()
    const isInvoking = shouldInvoke(time)

    lastArgs = args     //  args 是 event 對象,是點擊、scroll等事件傳過來的
    lastThis = this
    lastCallTime = time

    if (isInvoking) {
      if (timerId === undefined) {
        return leadingEdge(lastCallTime)
      }
      if (maxing) {
        // Handle invocations in a tight loop.
        timerId = startTimer(timerExpired, wait)
        return invokeFunc(lastCallTime)
      }
    }
    if (timerId === undefined) {
      timerId = startTimer(timerExpired, wait)
    }
    return result
  }

1500ms時scroll,開始執(zhí)行 debounced:

首先判斷shouldInvoke(time),因為第一次 lastCallTime === undefined 所以返回true;

并且此時 timerId === undefined, 所以執(zhí)行 leadingEdge(lastCallTime);

在 leadingEdge(lastCallTime) 函數(shù)中,設(shè)置 lastInvokeTime = time,這個挺關(guān)鍵的,并且設(shè)定一個 1000ms的定時器,如果leading 為true,則invokefunc,我們沒有設(shè)置leading這種情況不表~

1500ms~2500ms 之間沒什么事,定時器到點,執(zhí)行 invokeFunc(time);

invokeFunc 中再次設(shè)置 lastInvokeTime, 并重置 lastThis,lastArgs;

第一次 scroll 完畢,接下來是 3000ms,這種間隔很大的調(diào)用與單純的 debounce 沒有太大差別,4s結(jié)束會執(zhí)行 2次。

每 600ms 執(zhí)行一次:
先用文字描述吧:
首次進(jìn)入函數(shù)時因為 lastCallTime === undefined 并且 timerId === undefined,所以會執(zhí)行 leadingEdge,如果此時 leading 為 true 的話,就會執(zhí)行 func。同時,這里會設(shè)置一個定時器,在等待 wait(s) 后會執(zhí)行 timerExpired,timerExpired 的主要作用就是觸發(fā) trailing。

如果在還未到 wait 的時候就再次調(diào)用了函數(shù)的話,會更新 lastCallTime,并且因為此時 isInvoking 不滿足條件,所以這次什么也不會執(zhí)行。

時間到達(dá) wait 時,就會執(zhí)行我們一開始設(shè)定的定時器timerExpired,此時因為time-lastCallTime < wait,所以不會執(zhí)行 trailingEdge。

這時又會新增一個定時器,下一次執(zhí)行的時間是 remainingWait,這里會根據(jù)是否有 maxwait 來作區(qū)分:

如果沒有 maxwait,定時器的時間是 wait - timeSinceLastCall,保證下一次 trailing 的執(zhí)行。

如果有 maxing,會比較出下一次 maxing 和下一次 trailing 的最小值,作為下一次函數(shù)要執(zhí)行的時間。

最后,如果不再有函數(shù)調(diào)用,就會在定時器結(jié)束時執(zhí)行 trailingEdge。
簡單畫了個以時間為軸,函數(shù)執(zhí)行的情況:
看不懂的多看兩遍吧~~~

在沒配置其他參數(shù)的情況下,連續(xù)觸發(fā)也是不執(zhí)行,那我們增加一下 maxWait試一下:

function onScroll_1() {
   console.log("執(zhí)行滾動處理函數(shù)啦");  
}
window.addEventListener("scroll", debounce(onScroll_1, 1000, {
    maxWait: 1000
}));

文字描述過程:
首次進(jìn)入函數(shù)時因為 lastCallTime === undefined 并且 timerId === undefined,所以會執(zhí)行 leadingEdge,這里會設(shè)置一個定時器,在等待 wait(s) 后會執(zhí)行 timerExpired,timerExpired 的主要作用就是觸發(fā) trailing。

如果在還未到 wait 的時候就再次調(diào)用了函數(shù)的話,會更新 lastCallTime,并且因為此時 isInvoking 不滿足條件,所以這次什么也不會執(zhí)行。

時間到達(dá) wait 時,就會執(zhí)行我們一開始設(shè)定的定時器timerExpired,此時因為time-lastCallTime < wait,如果所以不會執(zhí)行 trailingEdge。但是如果設(shè)置了maxWait,這里還會判斷 time-lastInvokeTime > maxWait,(參考上圖中1600ms處,會執(zhí)行) 如果是則 trailingEdge。

這時又會新增一個定時器,下一次執(zhí)行的時間是 remainingWait,這里會根據(jù)是否有 maxwait 來作區(qū)分:

如果沒有 maxwait,定時器的時間是 wait - timeSinceLastCall,保證下一次 trailing 的執(zhí)行。

如果有 maxing,會比較出下一次 maxing 和下一次 trailing 的最小值,作為下一次函數(shù)要執(zhí)行的時間。

最后,如果不再有函數(shù)調(diào)用,就會在定時器結(jié)束時執(zhí)行 trailingEdge

常見問題,防抖函數(shù)如何傳參

其實糾結(jié)這個問題的同學(xué),看看函數(shù)式編程會理解一些~
其實很簡單,my_debounce會返回一個函數(shù),那在函數(shù)調(diào)用時加上參數(shù)就OK了~~

window.addEventListener("scroll", my_debounce(onScroll_1, 1000)("test"));

我們的 onScroll_1 這樣寫,就把"test" 傳給 params了。。

function onScroll_1(params) {
    console.log("onScroll_1", params);   // test
    console.log("執(zhí)行滾動處理函數(shù)啦");  
}

不過一般我們不會這樣寫吧,因為新傳的值會將 原來的 event 給覆蓋掉,也就拿不到 scroll 或者 mouseclick等事件對象 event 了~~
那你說,我既想獲取到 event對象,又想傳參,怎么辦?
我的辦法是,在自己的監(jiān)聽函數(shù)上動手腳,比如我的onScroll 函數(shù)這樣寫:

function onScroll(param) {
    console.log("param:", param);  // test
       return function(event) {
           console.log("event:", event);  // event
   }
}

如下這樣使用 debounce

window.addEventListener("scroll", my_debounce(onScroll("test"), 1000));

控制臺的日志確實如此~~

loadsh中throttle

有了 debounce的基礎(chǔ)loadsh對throttle的實現(xiàn)就非常簡單了,就是一個傳了 maxWait的debounce.

function throttle(func, wait, options) {
  let leading = true
  let trailing = true

  if (typeof func != "function") {
    throw new TypeError("Expected a function")
  }
  if (isObject(options)) {
    leading = "leading" in options ? !!options.leading : leading
    trailing = "trailing" in options ? !!options.trailing : trailing
  }
  return debounce(func, wait, {
    "leading": leading,
    "maxWait": wait,
    "trailing": trailing
  })
}

上面已經(jīng)分析了這種情況,它的結(jié)果是如果連續(xù)不斷觸發(fā)則每隔 wait 秒執(zhí)行一次func。

參考資料

輕松理解防抖

節(jié)流 防抖 場景辨析

從lodash源碼學(xué)習(xí)節(jié)流和防抖

聊聊lodash的debounce實現(xiàn)

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/99654.html

相關(guān)文章

  • [譯]通過實例講解DebouncingThrotting(防抖節(jié)流)

    摘要:譯通過實例講解和防抖與節(jié)流源碼中推薦的文章,為了學(xué)習(xí)英語,翻譯了一下原文鏈接作者本文來自一位倫敦前端工程師的技術(shù)投稿。首次或立即你可能發(fā)現(xiàn)防抖事件在等待觸發(fā)事件執(zhí)行,直到事件都結(jié)束后它才執(zhí)行。 [譯]通過實例講解Debouncing和Throtting(防抖與節(jié)流) lodash源碼中推薦的文章,為了學(xué)習(xí)(英語),翻譯了一下~ 原文鏈接 作者:DAVID CORBACHO 本文來自一位...

    Jenny_Tong 評論0 收藏0
  • 源碼分析】給你幾個鬧鐘,或許用 10 分鐘就能寫出 lodash debounce &

    摘要:最簡單的案例以最簡單的情景為例在某一時刻點只調(diào)用一次函數(shù),那么將在時間后才會真正觸發(fā)函數(shù)。后續(xù)我們會逐漸增加黑色鬧鐘出現(xiàn)的復(fù)雜度,不斷去分析紅色鬧鐘的位置。 序 相比網(wǎng)上教程中的 debounce 函數(shù),lodash 中的 debounce 功能更為強大,相應(yīng)的理解起來更為復(fù)雜; 解讀源碼一般都是直接拿官方源碼來解讀,不過這次我們采用另外的方式:從最簡單的場景開始寫代碼,然后慢慢往源碼...

    余學(xué)文 評論0 收藏0
  • 探究防抖(debounce)節(jié)流(throttle)

    摘要:如果使用的是防抖,那么得等我們停止?jié)L動之后一段時間才會加載新的內(nèi)容,沒有那種無限滾動的流暢感。這時候,我們就可以使用節(jié)流,將事件有效觸發(fā)的頻率降低的同時給用戶流暢的瀏覽體驗。調(diào)用,瀏覽器會在下次刷新的時候執(zhí)行指定回調(diào)函數(shù)。 本文來自我的博客,歡迎大家去GitHub上star我的博客 本文從防抖和節(jié)流出發(fā),分析它們的特性,并拓展一種特殊的節(jié)流方式requestAnimationFrame...

    keke 評論0 收藏0
  • 淺談節(jié)流防抖

    摘要:節(jié)流和防抖在開發(fā)項目過程中很常見,例如輸入實時搜索滾動更新了,等等,大量的場景需要我們對其進(jìn)行處理。防抖多次觸發(fā),只在最后一次觸發(fā)時,執(zhí)行目標(biāo)函數(shù)。節(jié)流限制目標(biāo)函數(shù)調(diào)用的頻率,比如內(nèi)不能調(diào)用次。 節(jié)流和防抖在開發(fā)項目過程中很常見,例如 input 輸入實時搜索、scrollview 滾動更新了,等等,大量的場景需要我們對其進(jìn)行處理。我們由 Lodash 來介紹,直接進(jìn)入主題吧。 Lod...

    thursday 評論0 收藏0
  • lodash源碼學(xué)習(xí)節(jié)流防抖

    摘要:首先重置防抖函數(shù)最后調(diào)用時間,然后去觸發(fā)一個定時器,保證后接下來的執(zhí)行。這就避免了手動管理定時器。 ??之前遇到過一個場景,頁面上有幾個d3.js繪制的圖形。如果調(diào)整瀏覽器可視區(qū)大小,會引發(fā)圖形重繪。當(dāng)圖中的節(jié)點比較多的時候,頁面會顯得異??D。為了限制類似于這種短時間內(nèi)高頻率觸發(fā)的情況,我們可以使用防抖函數(shù)。 ??實際開發(fā)過程中,這樣的情況其實很多,比如: 頁面的scroll事件 ...

    CloudDeveloper 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<