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

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

前言

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

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

防抖(debounce)

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

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

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

為了解決上述問題,我們在編碼中可以使用debounce防抖技術(shù)。

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

看一個??(栗子):

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

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

節(jié)流(throttle)

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

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

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

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

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

再看一個??:

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

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

應(yīng)用場景

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

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

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

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

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

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

總結(jié)

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

往期推薦

高頻JavaScript面試題匯總

?話題

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

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容