前端性能優(yōu)化的利器 ——— 淺談JavaScript中的防抖和節(jié)流

防抖和節(jié)流函數(shù)是工作中兩種常用的前端性能優(yōu)化函數(shù)箩艺,今天我就來(lái)總結(jié)一下什么是防抖和節(jié)流,并詳細(xì)說(shuō)明一下如何在工作中應(yīng)用防抖和節(jié)流函數(shù)

什么是防抖和節(jié)流?

在 JavaScript 中扒怖,防抖(debounce)和節(jié)流(throttle)是用來(lái)限制函數(shù)執(zhí)行頻率的兩種常見(jiàn)技術(shù)。

防抖(debounce) 是指在某個(gè)時(shí)間段內(nèi)狐蜕,只執(zhí)行最后一次觸發(fā)的函數(shù)調(diào)用煞聪。如果在這個(gè)時(shí)間段內(nèi)再次觸發(fā)該函數(shù),會(huì)重新計(jì)時(shí)膊夹,直到等待時(shí)間結(jié)束才會(huì)執(zhí)行函數(shù)衬浑。
這個(gè)技術(shù)通常用于處理頻繁觸發(fā)的事件,比如窗口大小調(diào)整放刨、搜索框輸入等工秩。防抖可以避免函數(shù)執(zhí)行過(guò)多次,以減少網(wǎng)絡(luò)開(kāi)銷和性能負(fù)擔(dān)进统。

節(jié)流(throttle) 是指在一段時(shí)間內(nèi)限制函數(shù)的執(zhí)行頻率助币,保證一定時(shí)間內(nèi)只執(zhí)行一次函數(shù)調(diào)用。無(wú)論觸發(fā)頻率多高螟碎,都會(huì)在指定時(shí)間間隔內(nèi)執(zhí)行一次函數(shù)眉菱。
這個(gè)技術(shù)通常用于處理連續(xù)觸發(fā)的事件,比如滾動(dòng)事件掉分、鼠標(biāo)移動(dòng)事件等俭缓。節(jié)流可以控制函數(shù)的執(zhí)行頻率克伊,以減少資源消耗和提高性能。

手寫一個(gè)防抖的工具函數(shù)

function debounce(func, delay) {
  let timeoutId;
  
  return function() {
    const context = this;
    const args = arguments;
    
    clearTimeout(timeoutId);
    
    timeoutId = setTimeout(function() {
      func.apply(context, args);
    }, delay);
  };
}

函數(shù)說(shuō)明

  1. 這個(gè)防抖函數(shù)接受兩個(gè)參數(shù):func表示需要進(jìn)行防抖的函數(shù)华坦,delay表示延遲的時(shí)間間隔(以毫秒為單位)
  2. 函數(shù)內(nèi)部使用了一個(gè)timeoutId變量來(lái)保存定時(shí)器的標(biāo)識(shí)愿吹。當(dāng)調(diào)用防抖函數(shù)返回的新函數(shù)時(shí),會(huì)清除之前的定時(shí)器季春,并設(shè)置一個(gè)新的定時(shí)器洗搂。只有在延遲時(shí)間內(nèi)沒(méi)有再次調(diào)用該新函數(shù)時(shí),才會(huì)觸發(fā)最終的函數(shù)執(zhí)行

使用示例

該示例表示在全局滾動(dòng)事件中使用防抖函數(shù)载弄,每200毫秒內(nèi)如果觸發(fā)滾動(dòng)事件耘拇,那么不會(huì)執(zhí)行handleScroll()函數(shù)。
然后重新計(jì)時(shí)200毫秒宇攻,再次判斷惫叛,直到最后一個(gè)200毫秒內(nèi)沒(méi)有觸發(fā)滾動(dòng)事件,才會(huì)執(zhí)行handleScroll()函數(shù)

function handleScroll() {
  console.log('Scrolled');
}
const debouncedScroll = debounce(handleScroll, 200);
window.addEventListener('scroll', debouncedScroll);

手寫一個(gè)節(jié)流的工具函數(shù)

function throttle(func, delay) {
  let timeoutId;
  let lastExecTime = 0;

  return function(...args) {
    const currentTime = Date.now();
    const remainingTime = delay - (currentTime - lastExecTime);

    clearTimeout(timeoutId);

    if (remainingTime <= 0) {
      func.apply(this, args);
      lastExecTime = currentTime;
    } else {
      timeoutId = setTimeout(() => {
        func.apply(this, args);
        lastExecTime = Date.now();
      }, remainingTime);
    }
  };
}

函數(shù)說(shuō)明

  1. 這個(gè)節(jié)流函數(shù)接受兩個(gè)參數(shù):func是要執(zhí)行的函數(shù)逞刷,delay是延遲時(shí)間(以毫秒為單位)
  2. 它返回一個(gè)新的函數(shù)嘉涌,該函數(shù)在調(diào)用時(shí)會(huì)根據(jù)指定的延遲時(shí)間來(lái)限制原始函數(shù)的執(zhí)行頻率

使用示例

在全局滾動(dòng)事件中使用節(jié)流函數(shù),無(wú)論在滾動(dòng)事件的監(jiān)聽(tīng)過(guò)程中夸浅,觸發(fā)了幾次handleScroll()函數(shù)仑最,都只會(huì)在每200毫秒內(nèi)執(zhí)行一次handleScroll()函數(shù)。

function handleScroll() {
  console.log('Scrolled');
}
const throttledScroll = throttle(handleScroll, 200);
window.addEventListener('scroll', throttledScroll);

如何在工作中應(yīng)用防抖和節(jié)流

防抖和節(jié)流主要應(yīng)用于:搜索框輸入事件監(jiān)聽(tīng)帆喇、窗口大小調(diào)整事件監(jiān)聽(tīng)警医、按鈕點(diǎn)擊事件監(jiān)聽(tīng)、滾動(dòng)事件監(jiān)聽(tīng)坯钦、鼠標(biāo)移動(dòng)事件監(jiān)聽(tīng)等等場(chǎng)景预皇。

工作中哪些場(chǎng)景可以使用防抖函數(shù)?

  1. 用戶輸入: 當(dāng)用戶在表單輸入框中頻繁輸入時(shí)婉刀,可以使用防抖函數(shù)來(lái)延遲處理用戶輸入吟温,避免頻繁的請(qǐng)求或操作,提高性能和用戶體驗(yàn)突颊。
  2. 搜索框: 在搜索框中鲁豪,當(dāng)用戶連續(xù)輸入關(guān)鍵字時(shí),可以使用防抖函數(shù)來(lái)延遲發(fā)送搜索請(qǐng)求律秃,以避免請(qǐng)求過(guò)多呈昔。
  3. 窗口調(diào)整: 當(dāng)窗口大小調(diào)整時(shí),會(huì)觸發(fā)resize事件友绝,可以使用防抖函數(shù)來(lái)限制resize事件的觸發(fā)次數(shù)堤尾,避免頻繁執(zhí)行調(diào)整相關(guān)的代碼。
  4. 按鈕頻繁點(diǎn)擊: 當(dāng)按鈕被頻繁點(diǎn)擊時(shí)迁客,可以使用防抖函數(shù)來(lái)限制按鈕點(diǎn)擊的觸發(fā)次數(shù)郭宝。

工作中哪些場(chǎng)景可以使用節(jié)流函數(shù)辞槐?

  1. 用戶輸入: 當(dāng)用戶在文本框中輸入時(shí),觸發(fā)搜索功能粘室。使用節(jié)流函數(shù)可以限制搜索請(qǐng)求的頻率榄檬,以避免頻繁的網(wǎng)絡(luò)請(qǐng)求。例如衔统,可以設(shè)置一個(gè)定時(shí)器鹿榜,在用戶輸入后的一小段時(shí)間內(nèi)不觸發(fā)搜索請(qǐng)求,只在定時(shí)器結(jié)束后才進(jìn)行搜索锦爵。
  2. 無(wú)限加載: 當(dāng)用戶滾動(dòng)頁(yè)面時(shí)舱殿,觸發(fā)加載更多數(shù)據(jù)的操作。使用節(jié)流函數(shù)可以限制加載操作的頻率险掀,以提高頁(yè)面的響應(yīng)性能沪袭。例如,可以設(shè)置一個(gè)定時(shí)器樟氢,在用戶滾動(dòng)過(guò)程中只觸發(fā)加載操作的最后一次滾動(dòng)事件冈绊。
  3. 按鈕頻繁點(diǎn)擊: 當(dāng)用戶頻繁點(diǎn)擊某個(gè)按鈕時(shí),觸發(fā)某個(gè)操作埠啃。使用節(jié)流函數(shù)可以限制點(diǎn)擊操作的頻率死宣,以避免重復(fù)操作或者混亂的界面狀態(tài)。例如碴开,可以設(shè)置一個(gè)定時(shí)器毅该,在用戶點(diǎn)擊后的一小段時(shí)間內(nèi)不觸發(fā)重復(fù)操作。

如何使用loadsh.js工具庫(kù)中的防抖和節(jié)流函數(shù)

實(shí)際開(kāi)發(fā)過(guò)程中叹螟,我們的項(xiàng)目中可能會(huì)直接使用loadsh.js工具庫(kù),來(lái)避免重復(fù)造輪子台盯,所以這里也特地說(shuō)明一下如何使用loadsh.js工具庫(kù)中的防抖和節(jié)流函數(shù)

  1. 安裝loadsh.js工具庫(kù)
npm install lodash  
  1. 在項(xiàng)目中引入loadsh.js工具庫(kù)罢绽,不同前端項(xiàng)目引入方式不同,請(qǐng)自行鑒別
import { debounce, throttle } from 'loadsh';
  1. 使用防抖函數(shù)静盅,該示例中良价,submitData()函數(shù)被限制為每1秒只會(huì)執(zhí)行最后一次,如果在等待時(shí)間內(nèi)多次調(diào)用該函數(shù)蒿叠,則會(huì)重置1秒的等待時(shí)間
// 定義要延遲執(zhí)行的函數(shù)
function submitData(data) {
  console.log('保存數(shù)據(jù):', data);
}

// 使用debounce函數(shù)創(chuàng)建一個(gè)延遲執(zhí)行的函數(shù)
const debouncedFn = _.debounce(submitData, 1000);

// 模擬連續(xù)觸發(fā)保存數(shù)據(jù)的操作
// 等待1秒后明垢,只會(huì)執(zhí)行最后一次保存數(shù)據(jù)的操作
debouncedFn('數(shù)據(jù)1'); // 不會(huì)輸出
debouncedFn('數(shù)據(jù)2'); // 不會(huì)輸出
debouncedFn('數(shù)據(jù)3'); //  輸出 —— 保存數(shù)據(jù):數(shù)據(jù)3

  1. 使用節(jié)流函數(shù),該示例中市咽,throttledFn()函數(shù)被限制為每秒只能執(zhí)行一次痊银,如果在等待時(shí)間內(nèi)多次調(diào)用該函數(shù),則不會(huì)執(zhí)行
// 定義要減少調(diào)用次數(shù)的函數(shù)
function fetchData(data) {
    console.log('拉取數(shù)據(jù):', data);
}

// 使用throttle函數(shù)創(chuàng)建一個(gè)定時(shí)執(zhí)行的函數(shù)
const throttledFn = _.throttle(fetchData, 2000);

// 調(diào)用throttledFn函數(shù)
throttledFn('數(shù)據(jù)1'); // 輸出 —— 拉取數(shù)據(jù): 數(shù)據(jù)1

// 在1秒內(nèi)多次調(diào)用throttledFn函數(shù)
throttledFn('數(shù)據(jù)2'); // 不會(huì)輸出

// 2秒后再次調(diào)用throttledFn函數(shù)
setTimeout(() => {
  throttledFn('數(shù)據(jù)3'); // 輸出 —— 拉取數(shù)據(jù): 數(shù)據(jù)3
}, 1000);

我是 fx67ll.com施绎,如果您發(fā)現(xiàn)本文有什么錯(cuò)誤溯革,歡迎在評(píng)論區(qū)討論指正贞绳,感謝您的閱讀!
如果您喜歡這篇文章致稀,歡迎訪問(wèn)我的 本文github倉(cāng)庫(kù)地址冈闭,為我點(diǎn)一顆Star,Thanks~ :)
轉(zhuǎn)發(fā)請(qǐng)注明參考文章地址抖单,非常感謝N堋!矛绘!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末耍休,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蔑歌,更是在濱河造成了極大的恐慌羹应,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件次屠,死亡現(xiàn)場(chǎng)離奇詭異园匹,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)劫灶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門裸违,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人本昏,你說(shuō)我怎么就攤上這事供汛。” “怎么了涌穆?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵怔昨,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我宿稀,道長(zhǎng)趁舀,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任祝沸,我火速辦了婚禮矮烹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘罩锐。我一直安慰自己奉狈,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布涩惑。 她就那樣靜靜地躺著仁期,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蟀拷,一...
    開(kāi)封第一講書(shū)人閱讀 51,301評(píng)論 1 301
  • 那天碰纬,我揣著相機(jī)與錄音,去河邊找鬼问芬。 笑死悦析,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的此衅。 我是一名探鬼主播强戴,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼挡鞍!你這毒婦竟也來(lái)了骑歹?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤墨微,失蹤者是張志新(化名)和其女友劉穎道媚,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體翘县,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡最域,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了锈麸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片镀脂。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖忘伞,靈堂內(nèi)的尸體忽然破棺而出薄翅,到底是詐尸還是另有隱情,我是刑警寧澤氓奈,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布翘魄,位于F島的核電站,受9級(jí)特大地震影響舀奶,放射性物質(zhì)發(fā)生泄漏暑竟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一伪节、第九天 我趴在偏房一處隱蔽的房頂上張望光羞。 院中可真熱鬧绩鸣,春花似錦怀大、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至捡多,卻和暖如春蓖康,著一層夾襖步出監(jiān)牢的瞬間铐炫,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工蒜焊, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留倒信,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓泳梆,卻偏偏與公主長(zhǎng)得像鳖悠,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子优妙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

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