JavaScript 函數(shù)防抖和函數(shù)節(jié)流.

JavaScript 函數(shù)防抖和函數(shù)節(jié)流.

在瀏覽器執(zhí)行環(huán)境中,等待主隊(duì)列任務(wù)(DOM TREE & CSS Tree & render Tree) 等任務(wù)執(zhí)行完畢之后.

就開(kāi)始執(zhí)行 EventLoop 環(huán)境事件了.

所以,某種程度上,瀏覽器是基于事件驅(qū)動(dòng)的

既然提到了事件,那么我們最常見(jiàn)的就是給一些元素綁定一些事件了.

比如給一個(gè)按鈕綁定一個(gè)click事件.


給元素綁定一個(gè)事件

給元素綁定一個(gè)事件,是一個(gè)非常常見(jiàn)的場(chǎng)景.

btn.onclick = function (e) {
    console.log('xxxxx')
}

這沒(méi)什么大不了的.


函數(shù)節(jié)流

先不要管什么是函數(shù)節(jié)流.

上述我們給一個(gè)按鈕添加了一個(gè)點(diǎn)擊事件.

每次點(diǎn)擊這個(gè)按鈕的時(shí)候,都會(huì)觸發(fā)這個(gè)事件響應(yīng)函數(shù).

這個(gè)是沒(méi)毛病的.

之前也都是這么做的.

但是如果,一個(gè)哥們單身30年,手速很快

假如這個(gè)哥們一秒能點(diǎn)擊100下. 且這又是一個(gè)發(fā)送ajax請(qǐng)求的按鈕.

那么,每一秒鐘

  • 前端瀏覽器,這個(gè)按鈕的事件響應(yīng)函數(shù)執(zhí)行了100次.發(fā)送了100個(gè)請(qǐng)求.
  • 后臺(tái)服務(wù)器每一秒接受到了100個(gè)請(qǐng)求.并處理這100個(gè)請(qǐng)求.

現(xiàn)在有一個(gè)需求是,這個(gè)按鈕一秒鐘不管點(diǎn)擊多少下,只能執(zhí)行一下.(你手速在快也沒(méi)有用)

這個(gè)需求描述的就是函數(shù)節(jié)流.

某些函數(shù)可能會(huì)出現(xiàn)非常頻繁的調(diào)用,但是在某一個(gè)周期內(nèi),不管觸發(fā)多少次,實(shí)際上只能被執(zhí)行一次.

所以需要這么一個(gè)機(jī)制:

  • 一個(gè)函數(shù)被執(zhí)行了.(此時(shí)計(jì)時(shí)是A)
  • 這個(gè)函數(shù)又被執(zhí)行了.(此時(shí)計(jì)時(shí)是B)
  • 如果(B-A<等待值),那么這個(gè)函數(shù)就不執(zhí)行.
  • 否則就執(zhí)行.
<h4>節(jié)流</h4>
<p>節(jié)流就是指函數(shù)大于等于某個(gè)時(shí)間周期才能執(zhí)行.否則就不執(zhí)行.一個(gè)周期范圍內(nèi)只能處理一次.</p>
<div id="show">0</div>
<button id='jl'>函數(shù)節(jié)流</button>
  let canClick = true
  let counter = 0
  document.getElementById('jl').addEventListener('click', function () {
    if (canClick) {
      canClick = false
      console.log(++counter)
      setTimeout(() => {
        canClick = true
      }, 1000);
    }
  }, false)

利用一個(gè) canClick 變量來(lái)標(biāo)記這個(gè)按鈕是否被點(diǎn)擊.

并在一個(gè) setTimeout 的定時(shí)器里,定時(shí)1秒去修改這個(gè) canClick

于是,目的就達(dá)到了..每次點(diǎn)擊之后,下一次點(diǎn)擊有效必須要等待一秒.

但這樣的做法,不具備通用性.

可以封裝一個(gè)函數(shù)節(jié)流的方法,把需要節(jié)流的函數(shù)通過(guò)這個(gè)方法封裝.

function throttle (handler, wait) {
    let lastTime = 0
    return function () {
        let nowTime = new Date().getTime()
        if (nowTime - lastTime > wait) {
            hanlder.apply(this,arguments)
            lastTime = nowTime
        }
    }
}
 function hanlder (e) {
    e.stopPropagation()
    show.innerText = parseInt(show.innerText) + 1
}

jl.addEventListener('click', throttle(hanlder, 1000), false)

函數(shù)節(jié)流:

  • 這個(gè)函數(shù)可能存在頻繁執(zhí)行的情況(手速飛快,點(diǎn)擊按鈕)
  • 我們希望這個(gè)函數(shù)在某個(gè)周期內(nèi)只執(zhí)行一次.

函數(shù)防抖

其實(shí)和函數(shù)節(jié)流一樣.

函數(shù)防抖也是用于解決某些函數(shù)頻繁觸發(fā)的情況.

但不同的是:

  • 函數(shù)節(jié)流,指的是在某一個(gè)周期內(nèi),只能執(zhí)行一次此函數(shù)
  • 函數(shù)防抖,指的是必須超過(guò)某一個(gè)時(shí)間閾值,否則函數(shù)不執(zhí)行.

一個(gè)比較常見(jiàn)的場(chǎng)景.

在文本框內(nèi)輸入搜索字符,并實(shí)時(shí)的發(fā)送搜索關(guān)鍵字到后臺(tái).

正常情況下,一般都是這么寫(xiě)的.

<input type="text" name="" id="inp">
 inp.addEventListener('input', function () {
    console.log(this.value)
  }, false)
image.png

發(fā)現(xiàn)每次輸入一個(gè)文本都會(huì)觸發(fā)一次input事件響應(yīng)函數(shù).

此函數(shù)的觸發(fā)頻率完全取決于用戶在輸入框中,輸入的文本快慢.

這沒(méi)有什么問(wèn)題,input 事件就是這么定義的.

但是對(duì)于實(shí)際場(chǎng)景而言,可能就出現(xiàn)了如下的不足:

  • 每次文本變動(dòng),都會(huì)觸發(fā) input 從而觸發(fā)后臺(tái)的請(qǐng)求操作.
  • 對(duì)于用于而言,可能需要查詢的是 123321 字符串. 而文本的輸入會(huì)導(dǎo)致之前的5次查詢都是無(wú)效的.

所以,這里就需要就一個(gè)機(jī)制..

  • 事件函數(shù)不是立馬執(zhí)行.
  • 會(huì)等待一段時(shí)間
  • 如果在等待的這一段事件內(nèi),事件函數(shù)又被觸發(fā)了.
  • 那么上一次的事件函數(shù)就不會(huì)執(zhí)行.
  • 接著等待一段時(shí)間.
  • 如此循環(huán)
  • 直到等待時(shí)間超過(guò)了,且用戶沒(méi)有操作了.我在執(zhí)行這個(gè)事件函數(shù).

這個(gè)就是所謂的函數(shù)節(jié)流.

除非函數(shù)不觸發(fā)了,且超過(guò)某個(gè)時(shí)間閾值,否則之前的事件都不會(huì)觸發(fā).

代碼改寫(xiě)為:

 let inp = document.getElementById('inp')
  let timer = null
  inp.addEventListener('input', function () {
    clearTimeout(timer)
    timer = setTimeout(() => {
      console.log(this.value)  
    }, 1000);
    
  }, false)

結(jié)果:

image.png

只有等待用戶操作停止了躁劣,并達(dá)到某一個(gè)等待的時(shí)間閾值,再最后觸發(fā)事件響應(yīng)函數(shù).

上述的代碼不具備通用性.

封裝函數(shù)節(jié)流方法 debounce

function debounce (hanlder, wait) {
    let timer = null
    return function () {
        clearTimeout(timer)
        timer = setTimeout(() => {
            hanlder.apply(this,arguments)
        }, wait)
    }
}

所以整體代碼可以改寫(xiě)為

 function inputHanlder(e) {
    console.log(this.value, e)
  }
  // 函數(shù)防抖 , 頻繁
  function debounce(hanlder, wait) {
    let timer = null
    return function () {
      clearTimeout(timer)
      timer = setTimeout(() => {
        hanlder.apply(this, arguments)
      }, wait);
    }
  }

  inp.addEventListener('input',debounce(inputHanlder,1000), false)

總結(jié)

  • 不管是函數(shù)防抖還是函數(shù)節(jié)流,都是為了解決函數(shù)頻繁執(zhí)行的問(wèn)題.
  • 函數(shù)防抖:函數(shù)在單位時(shí)間內(nèi),只會(huì)被觸發(fā)一次.
  • 函數(shù)節(jié)流:函數(shù)只有在超過(guò)了某個(gè)時(shí)間閾值后才會(huì)被執(zhí)行.否則函數(shù)不執(zhí)行.

一個(gè)只執(zhí)行一次(函數(shù)節(jié)流).

一個(gè)不滿足條件就一次也不執(zhí)行(函數(shù)防抖).

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末愁茁,一起剝皮案震驚了整個(gè)濱河市允青,隨后出現(xiàn)的幾起案子捷兰,更是在濱河造成了極大的恐慌浙巫,老刑警劉巖嗅绸,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件搞疗,死亡現(xiàn)場(chǎng)離奇詭異嗤朴,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)沐飘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門游桩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人耐朴,你說(shuō)我怎么就攤上這事借卧。” “怎么了筛峭?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵铐刘,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我蜒滩,道長(zhǎng)滨达,這世上最難降的妖魔是什么奶稠? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮捡遍,結(jié)果婚禮上锌订,老公的妹妹穿的比我還像新娘。我一直安慰自己画株,他們只是感情好辆飘,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著谓传,像睡著了一般蜈项。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上续挟,一...
    開(kāi)封第一講書(shū)人閱讀 51,521評(píng)論 1 304
  • 那天紧卒,我揣著相機(jī)與錄音,去河邊找鬼诗祸。 笑死跑芳,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的直颅。 我是一名探鬼主播博个,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼功偿!你這毒婦竟也來(lái)了盆佣?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤械荷,失蹤者是張志新(化名)和其女友劉穎共耍,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體养葵,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡征堪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了关拒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡庸娱,死狀恐怖着绊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情熟尉,我是刑警寧澤归露,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站斤儿,受9級(jí)特大地震影響剧包,放射性物質(zhì)發(fā)生泄漏恐锦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一疆液、第九天 我趴在偏房一處隱蔽的房頂上張望一铅。 院中可真熱鬧,春花似錦堕油、人聲如沸潘飘。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)卜录。三九已至,卻和暖如春眶明,著一層夾襖步出監(jiān)牢的瞬間艰毒,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工搜囱, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留现喳,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓犬辰,卻偏偏與公主長(zhǎng)得像嗦篱,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子幌缝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 函數(shù)節(jié)流 還記得上篇文章中說(shuō)到的圖片懶加載嗎灸促?我們?cè)谖恼碌淖詈髮?shí)現(xiàn)了一個(gè)頁(yè)面滾動(dòng)時(shí)按需加載圖片的方式,即在觸發(fā)滾動(dòng)...
    柏丘君閱讀 2,846評(píng)論 1 19
  • 在日常開(kāi)發(fā)中涵卵,我們經(jīng)常能夠碰到以下工作場(chǎng)景: 對(duì)提交按鈕進(jìn)行變態(tài)的點(diǎn)擊壓力測(cè)試輸入框內(nèi)容的實(shí)時(shí)校驗(yàn)(譬如驗(yàn)證用戶名...
    叫我小徐閱讀 1,007評(píng)論 0 5
  • 前言 最近和前端的小伙伴們浴栽,在討論面試題的時(shí)候。談到了函數(shù)防抖和函數(shù)節(jié)流的應(yīng)用場(chǎng)景和原理轿偎。于是典鸡,想深入研究一下兩者...
    youthcity閱讀 23,545評(píng)論 5 78
  • 我覺(jué)得我偶爾孩子氣很重,我想了想坏晦,這是好事萝玷,跟舒服的人相處才會(huì)有的樣子,我一直想把這種狀態(tài)拾回來(lái)
    茶花雨閱讀 173評(píng)論 0 0
  • 綜藝類: 王牌對(duì)王牌:我也是前幾天的時(shí)候看到的昆婿,室友推薦我去看的球碉,真的是超搞笑,我現(xiàn)在在看第四季仓蛆,每一期都很好睁冬,有...
    梁家_大少爺閱讀 216評(píng)論 0 1