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

資訊專欄INFORMATION COLUMN

由淺入深學習lodash的debounce函數(shù)

Raaabbit / 875人閱讀

摘要:定時器調(diào)用頻率優(yōu)化把開啟定時器的邏輯放在可以大大減少定時器的數(shù)量。舉個例子,比如為,此時在某一個定時器的回調(diào)函數(shù)檢測到上一次觸法事件的為,而為,此時雖然要開啟下一次定時,但這個時候定時的時間為就可以了。

最近的面試中考到了debounce,函數(shù)防抖,筆試的時候答的不是特別好,下來好好研究了一下,從原理到優(yōu)化,再到開源工具庫lodash的實現(xiàn)源碼,梳理了一番,現(xiàn)整理如下。

先簡單介紹一下debounce,從最簡單的一個場景入手,當用戶不斷點擊頁面,短時間內(nèi)頻繁的觸法點擊事件,只有在用戶觸法事件后的ns時間內(nèi),沒有再觸法事件,真正的監(jiān)聽函數(shù)才會執(zhí)行,如果在這段時間內(nèi)再次觸法了事件,就需要重新計算這個ns。

debounce最主要的作用是把多個觸法事件的操作延遲到最后一次觸法執(zhí)行,在性能上做了一定的優(yōu)化。

不使用debounce

如果不使用debounce,那就會每一次點擊都會觸法事件的回調(diào)函數(shù),這有時候?qū)τ谛阅苁且环N巨大的浪費(比如大量的增加dom元素)?;蛘弋敾卣{(diào)函數(shù)計算量很大的時候,甚至會導(dǎo)致阻塞。

window.addEventListener("click", function (event) {
  var p = document.createElement("p")
  p.innerHTML = "trigger"
  document.body.appendChild(p)
})

頻繁觸法
可以看出,每一次點擊都會觸法函數(shù)執(zhí)行。

使用debounce
window.addEventListener("click", debounce(function (event) {
    var p = document.createElement("p")
    p.innerHTML = "trigger"
    document.body.appendChild(p)
    return "aaaa"
}, 500))

debounce優(yōu)化
可以看出,只有在最后一次點擊的500ms后,真正的函數(shù)func才會觸法。

開始實現(xiàn)debounce

本篇文章的debounce實現(xiàn)主要參考了lodash庫,會從最基礎(chǔ)的實現(xiàn)開始,一步步完善它。
debounce的核心實現(xiàn),就是要判斷每次觸法事件的時候,要不要執(zhí)行真正的func。

大體思路就是每次觸法事件都開啟一個延時的定時器,在定時器結(jié)束的時候?qū)Ρ扰c最后一次觸法事件時的時間差,如果時間差大于延遲的閾值,那么就執(zhí)行真正的func`。

大致的結(jié)構(gòu)如下

function debounce (func, wait) {
    var lastCallTime   // 最后一次觸法事件的時間
    var lastThis       // 作用域
    var lastArgs       // 參數(shù)
    var timerId        // 定時器對象
    wait = +wait || 0
    // 啟動定時器
    function startTimer (timerExpired, wait) {
        return setTimeout(timerExpired, wait)
    }
    
    // func函數(shù)執(zhí)行   
    function invokeFunc () {
    
    }
    
    // 調(diào)用func函數(shù)的判定條件 
    function shouldInvoke () {
    
    }
    
    //  定時器的回調(diào)函數(shù) 
    function timerExpired () {
        // 在這里判斷觸法事件的時間差
    }
    
    // 要返回的函數(shù)
    function debounced (...args) {
    
    }
    
    return debounced
}

這就是基本的debounce函數(shù)的構(gòu)成,下面邊解析,邊去一一填充這些函數(shù),最后再對函數(shù)進行一步步的優(yōu)化。

debounced

每一次觸法事件的時候都會進入到這個函數(shù),這個函數(shù)需要做這么幾個事情。

確定作用域和參數(shù)

更新觸法事件的時間,也就是lastCallTime

啟動定時器 timerId

function debounced (...args) {
    const time = Date.now()
    lastThis = this
    lastArgs = args
    lastCallTime = time
    timerId = startTimer(timerExpired, wait)
}
startTimer

startTimer 就是啟動一個定時器,后續(xù)會有更多的拓展,所以封裝一個函數(shù)

function startTimer (timerExpired, wait) {
    return setTimeout(timerExpired, wait)
}
timerExpired

timerExpired 主要判斷是否執(zhí)行func

function timerExpired () {
    const time = Date.now()
    if (shouldInvoke(time)) {
        return invokeFunc()
    }
}
shouldInvoke

shouldInvoke判斷每次事件觸法的時間差,如果大于閾值,那么真正的func就會執(zhí)行

function shouldInvoke (time) {
    return lastCallTime !== undefined && (time - lastCallTime >= wait)
}
invokeFunc
function invokeFunc () {
    timerId = undefined
    const args = lastArgs
    const thisArg = lastThis
    let result = func.apply(thisArg, args)
    lastArgs = lastThis = undefined
    return result
}

這樣,這個函數(shù)就寫完了。把每一步拆解開來,理解還是相對容易的,再總結(jié)一下。每一次觸法事件,都開啟一個定時器timerId,并且會更新觸法事件的最后時間lastCallTime,在定時器的回調(diào)函數(shù)里面,判斷回調(diào)函數(shù)的執(zhí)行時間與lastCallTime的時間差,如果大于閾值,說明延遲時間到了,func執(zhí)行,如果小于,就忽略。

優(yōu)化

雖然實現(xiàn)了基本的debounce,但在擴展它的功能之前,看一看有沒有優(yōu)化的空間,每一次觸法事件都開啟一個定時器是不是太浪費了。這里可不可以減少調(diào)用次數(shù)。

定時器調(diào)用頻率優(yōu)化

把開啟定時器的邏輯放在timerExpired可以大大減少定時器的數(shù)量。debounced開啟了第一次定時器后,debounced會忽略后面的定時器開啟,直到func執(zhí)行之后(timerIdundefined),而在timerExpired里面判斷如果func不滿足觸發(fā)條件,那么就開啟下一個定時器。

其實本質(zhì)就是確保上一個定時器的回調(diào)不會觸法func了,才會開啟下一個定時器。

優(yōu)化代碼如下

function timerExpired () {
    const time = Date.now()
    if (shouldInvoke(time)) {
        return invokeFunc()
    }
    timerId = startTimer(timerExpired, wait)
}
function debounced (...args) {
    const time = Date.now()
    lastThis = this
    lastArgs = args
    lastCallTime = time
    if (timerId === undefined) {
        timerId = startTimer(timerExpired, wait)
    }
}
定時器時間的優(yōu)化

timerExpired 中開啟的定時器

timerId = startTimer(timerExpired, wait)

延遲的時間是否一定為wait呢,這是不一定的。
舉個例子,比如wait5,此時在某一個定時器的回調(diào)函數(shù)timerExpired檢測到上一次觸法事件的lastCallTime100,而Date.now()103,此時雖然103-100 = 3 < 5,要開啟下一次定時,但這個時候定時的時間為 5 - 3 = 2就可以了。這才是精確的時間。

所以我們需要把這個時間封裝成一個函數(shù)remainingWait

function remainingWait(time) {
    const timeSinceLastCall = time - lastCallTime
    const timeWaiting = wait - timeSinceLastCall
    return timeWaiting
}
function timerExpired () {
    const time = Date.now()
    if (shouldInvoke(time)) {
        return invokeFunc()
    }
    timerId = startTimer(timerExpired, remainingWait(time))
}

附上執(zhí)行的流程圖

總結(jié)

這其實只是實現(xiàn)了一個basicDebounce,其實有的時候我們需要在頻繁觸法事件的開始立即執(zhí)行func,而忽略后面的觸法事件,這就需要加入?yún)?shù)控制,也就是lodash中的trailingleading,甚至兩者同時存在,頭尾各執(zhí)行一次,還有就是throttle函數(shù)節(jié)流,保證在一段時間內(nèi)func至少執(zhí)行一次,這就是lodash中的maxWait參數(shù)。下一篇文章會完善這些功能,屆時,一個完整的debounce才是真正的實現(xiàn)了。

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

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

相關(guān)文章

  • lodash源碼學習節(jié)流與防抖

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

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

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

    余學文 評論0 收藏0
  • [譯]通過實例講解Debouncing和Throtting(防抖與節(jié)流)

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

    Jenny_Tong 評論0 收藏0
  • JS throttle與debounce區(qū)別

    摘要:可以看下面的栗子這個圖中圖中每個小格大約,右邊有原生事件與節(jié)流去抖插件的與事件。即如果有連續(xù)不斷的觸發(fā),每執(zhí)行一次,用在每隔一定間隔執(zhí)行回調(diào)的場景。執(zhí)行啦打印執(zhí)行啦打印執(zhí)行啦節(jié)流按照上面的說明,節(jié)流就是連續(xù)多次內(nèi)的操作按照指定的間隔來執(zhí)行。 一般在項目中我們會對input、scroll、resize等事件進行節(jié)流控制,防止事件過多觸發(fā),減少資源消耗;在vue的官網(wǎng)的例子中就有關(guān)于lod...

    wawor4827 評論0 收藏0
  • 快速 TypeScript 化 lodash throttle & debounce

    摘要:背景需要包寫起來爽,然而如果遇到?jīng)]有現(xiàn)成的化的工具函數(shù),就需要自己想辦法弄出一份類型聲明文件了。最為重要的是,這種遷移方面我們可以隨意自定義化中所需要的工具函數(shù),遷移粒度都可以由自己控制。 1、背景 1.1、需要 TS 包 TypeScript 寫起來爽,然而如果遇到?jīng)]有現(xiàn)成的 TS 化的工具函數(shù),就需要自己想辦法弄出一份類型聲明文件了。 前兩天要寫的小工具庫(Typescript 語...

    lewinlee 評論0 收藏0

發(fā)表評論

0條評論

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