JavaScript中的節(jié)流與防抖函數(shù)記不住?我的答案保你終身難忘【一小時(shí)搞懂,建議收藏】

前端性能優(yōu)化-防抖和節(jié)流

前言

防抖和節(jié)流嚴(yán)格算起來(lái)應(yīng)該屬于性能優(yōu)化的知識(shí),但實(shí)際上遇到的頻率相當(dāng)高,處理不當(dāng)或者放任不管就容易引起瀏覽器卡死。所以還是很有必要早點(diǎn)掌握的。

據(jù)說(shuō)阿里有一道面試題就是談?wù)労瘮?shù)節(jié)流和函數(shù)防抖。

防抖(debounce)

函數(shù)防抖(debounce):當(dāng)持續(xù)觸發(fā)事件時(shí),在設(shè)置的周期內(nèi)沒(méi)有再觸發(fā)事件,事件處理函數(shù)才會(huì)執(zhí)行一次,如果設(shè)定的周期沒(méi)有結(jié)束,又一次觸發(fā)了事件,就重新開(kāi)始延時(shí)。

為了有個(gè)直觀的對(duì)比,我們先看下沒(méi)有使用debounce技術(shù)的click事件:

我們看到,當(dāng)用戶頻繁點(diǎn)擊button按鈕時(shí),控制臺(tái)會(huì)頻繁的輸出結(jié)果,這種頻繁調(diào)用事件處理程序,會(huì)加重瀏覽器的負(fù)擔(dān),導(dǎo)致用戶體驗(yàn)非常糟糕。

為了解決上述問(wèn)題,我們?cè)诰幋a中可以使用debounce防抖技術(shù)。

防抖原理:是維護(hù)一個(gè)計(jì)時(shí)器,在規(guī)定的delay時(shí)間后觸發(fā)函數(shù),但是在delay時(shí)間內(nèi)再次觸發(fā)的話,就會(huì)取消之前的計(jì)時(shí)器而重新設(shè)置。這樣一來(lái),只有最后一次操作能被觸發(fā)。

看一個(gè)??(栗子):

function debounce(fn, delay) {
  var timer = null;
  return function() {
     // 清除已存在的定時(shí)器
     timer && clearTimeout(timer)
     timer = setTimeout(function() {
        fn.apply(this)
     }, delay)
  }
}
let $btn = document.getElementById('btn');
var fn = function() {
  console.log ('防抖旨在時(shí)間段內(nèi)只觸發(fā)最后一次執(zhí)行' + new Date(Date.now()));
}
$btn.onclick = debounce(fn, 1000);

如圖,持續(xù)觸發(fā)click事件時(shí),并不會(huì)每次觸發(fā)都會(huì)執(zhí)行事件處理函數(shù),當(dāng)在設(shè)置的周期1000 ms內(nèi)沒(méi)有再觸發(fā)click事件時(shí),才會(huì)延時(shí)觸發(fā)click事件。

節(jié)流(throttle)

函數(shù)節(jié)流(throttle):函數(shù)執(zhí)行一次后,只有在大于設(shè)置的執(zhí)行周期后才會(huì)執(zhí)行第二次。持續(xù)觸發(fā)事件時(shí),保證一定時(shí)間段內(nèi)只調(diào)用一次事件處理函數(shù)。

throttle翻譯為節(jié)流閥,我們可以想象成我們水龍頭放水,閥門(mén)一打開(kāi),水嘩嘩的往下流,秉著勤儉節(jié)約的優(yōu)良傳統(tǒng)美德,我們要把水龍頭關(guān)小點(diǎn),最好是如我們心意按照一定規(guī)律在某個(gè)時(shí)間間隔內(nèi)一滴一滴的往下滴。

同樣我們先看一個(gè)沒(méi)有使用throttle技術(shù)的scroll事件,如下圖:

這種頻繁的scroll操作都會(huì)給瀏覽器帶來(lái)沉重的負(fù)擔(dān),接下來(lái)我們看下如何使用throttle技術(shù)。

節(jié)流原理:是記錄上次執(zhí)行的時(shí)間戳lastTime,每次觸發(fā)事件時(shí)記錄當(dāng)前執(zhí)行的時(shí)間戳nowTime,然后判斷nowTime與lastTime的差值是否大于設(shè)定的周期delay,如果大于,則執(zhí)行回調(diào),并更新上次執(zhí)行的時(shí)間戳,從而循環(huán)。持續(xù)觸發(fā)事件時(shí),保證一定時(shí)間段內(nèi)觸發(fā)事件處理函數(shù)的頻率。

再看一個(gè)??:

function throttle(fn, delay) {
  // 記錄上次觸發(fā)的時(shí)間戳
  var lastTime = 0;
  return function() {
     // 記錄當(dāng)前觸發(fā)的時(shí)間戳
     var nowTime = Date.now();
     // 如果當(dāng)前觸發(fā)與上次觸發(fā)的時(shí)間差值 大于 設(shè)置的周期則允許執(zhí)行
     if (nowTime - lastTime > delay) {
        fn.call(this);
        // 更新時(shí)間戳
        lastTime = nowTime;
     }
  }
}
document.onscroll = function () {
  console.log ('節(jié)流旨在時(shí)間段內(nèi)控制觸發(fā)的頻率'+new Date(Date.now()))
}

如下圖,持續(xù)觸發(fā)scroll事件時(shí),并不立即執(zhí)行處理函數(shù),當(dāng)當(dāng)前觸發(fā)與上次觸發(fā)的時(shí)間差值大于設(shè)置的周期時(shí)才會(huì)執(zhí)行。

應(yīng)用場(chǎng)景

上面介紹了防抖(debounce)節(jié)流(throttle) 的原理和實(shí)現(xiàn)方式。

下面簡(jiǎn)單列出兩者的應(yīng)用場(chǎng)景都有哪些:

防抖(debounce)應(yīng)用場(chǎng)景:

  • 每個(gè)調(diào)整大小/滾動(dòng)都會(huì)觸發(fā)統(tǒng)計(jì)事件。
  • 驗(yàn)證文本輸入(在連續(xù)文本輸入后,發(fā)送Ajax請(qǐng)求進(jìn)行驗(yàn)證)。
  • 監(jiān)視滾動(dòng)scroll事件(在添加去抖動(dòng)后滾動(dòng),只有在用戶停止?jié)L動(dòng)后才會(huì)確定它是否已到達(dá)頁(yè)面底部)。

節(jié)流(throttle)應(yīng)用場(chǎng)景:

  • 實(shí)現(xiàn)DOM元素的拖放功能mousemove。
  • 搜索關(guān)聯(lián)keyup。
  • 計(jì)算鼠標(biāo)移動(dòng)距離mousemove。
  • 畫(huà)布模擬草圖功能mousemove。
  • 射擊游戲中的 mousedown/keydown事件(每單位時(shí)間只能發(fā)射一顆- 子彈)。
  • 監(jiān)視滾動(dòng)scroll事件(添加節(jié)流后,只要滾動(dòng)頁(yè)面,就會(huì)每隔一段時(shí)間才會(huì)計(jì)算)。

總結(jié)

  • 函數(shù)防抖和函數(shù)節(jié)流都是防止某一時(shí)間頻繁觸發(fā),但是這兩兄弟之間的原理卻不一樣。
  • 函數(shù)防抖是某一段時(shí)間內(nèi)只執(zhí)行一次,而函數(shù)節(jié)流是間隔時(shí)間執(zhí)行。

往期推薦

高頻JavaScript面試題匯總

?話題

什么樣的答案終身難忘?學(xué)生時(shí)代關(guān)于記憶經(jīng)常能聽(tīng)見(jiàn)兩種論調(diào):
1.死記硬背:見(jiàn)效快,但也忘得快,且一般不會(huì)靈活運(yùn)用(指標(biāo)不治本)
2.理解性記憶:見(jiàn)效慢,但記憶持久且會(huì)靈活運(yùn)用(治標(biāo)又治本)

如果是你,你愿意pick哪種?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 一、前言 相信無(wú)論在實(shí)際應(yīng)用場(chǎng)景、亦或是面試,都會(huì)經(jīng)常遇得到函數(shù)防抖、函數(shù)節(jié)流等,下面我們來(lái)聊一聊吧。 先放出一個(gè)...
    越前君閱讀 937評(píng)論 0 15
  • 摘要:JavaScript 中的函數(shù)防抖和節(jié)流是很常用的需求,前端面試也是頻率比較高被問(wèn)到的知識(shí),本文就通過(guò)二者的...
    EnochQin閱讀 1,259評(píng)論 0 5
  • 防抖:就是指觸發(fā)事件后在 n 秒內(nèi)函數(shù)只能執(zhí)行一次,如果在 n 秒內(nèi)又觸發(fā)了事件,則會(huì)重新計(jì)算函數(shù)執(zhí)行時(shí)間。(常見(jiàn)...
    半日閑_balance閱讀 207評(píng)論 0 0
  • 一、背景 防抖和節(jié)流是兩種不同的控制一個(gè)函數(shù)執(zhí)行次數(shù)的方法,其目的都是為了節(jié)約計(jì)算機(jī)資源。當(dāng)我們操作DOM的時(shí)候,...
    HalShaw閱讀 805評(píng)論 0 0
  • 表情是什么,我認(rèn)為表情就是表現(xiàn)出來(lái)的情緒。表情可以傳達(dá)很多信息。高興了當(dāng)然就笑了,難過(guò)就哭了。兩者是相互影響密不可...
    Persistenc_6aea閱讀 125,987評(píng)論 2 7