前端優(yōu)化 —— 函數(shù)的節(jié)流和防抖

參考https://www.pandashen.com/2017/06/12/20170612130942/

前言

在前端開發(fā)當(dāng)中我們經(jīng)常會綁定一些事件觸發(fā)的某些程序執(zhí)行,有時(shí)這些事件會連續(xù)觸發(fā)模叙,如瀏覽器窗口 scroll、resize,輸入框的 keyup抵乓、input男杈,以及 click 事件在連續(xù)點(diǎn)擊時(shí)連續(xù)發(fā)送請求等等柑司,這些情況有些會嚴(yán)重影響前端性能,有些會增加服務(wù)器壓力景醇,使用戶體驗(yàn)大打折扣,而函數(shù)節(jié)流和防抖就是為了解決這樣的問題吝岭。

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

函數(shù)節(jié)流:當(dāng)持續(xù)發(fā)生事件時(shí)三痰,保證在一個(gè)固定的時(shí)間間隔只執(zhí)行一次真正的事件處理程序,通俗的說就像 “節(jié)流” 的名字一樣窜管,打開水龍頭時(shí)要秉承勤儉節(jié)約的原則散劫,把閥門關(guān)小,最好是達(dá)到在固定間隔內(nèi)水一滴一滴的往下流幕帆。

節(jié)流函數(shù)的時(shí)序圖

fun-throtter.png

從圖中可以看出获搏,連續(xù)觸發(fā)事件時(shí),真正執(zhí)行事件處理程序的間隔是固定的蜓肆,多次觸發(fā)颜凯,也只會在某一個(gè)時(shí)間間隔內(nèi)觸發(fā)一次谋币,由于事件處理函數(shù)內(nèi)部執(zhí)行邏輯各不相同,我們就封裝一版可通用的節(jié)流函數(shù)症概。

節(jié)流函數(shù)的封裝

文件:throtter.js

// 節(jié)流函數(shù)
const throtter = (func, delay = 60) => {
    // 鎖的標(biāo)識
    let lock = false;

    // 返回一個(gè)事件處理函數(shù)
    return (...args) => {
        // 如果 lock 為 true 則跳出
        if (lock) return;

        // 執(zhí)行函數(shù)并更改鎖的狀態(tài)
        func(...args);
        lock = true;

        // 添加定時(shí)器蕾额,在到達(dá)時(shí)間間隔時(shí)重置鎖的狀態(tài)
        setTimeout(() => lock = false, delay);
    }
}

throtter 函數(shù)有兩個(gè)參數(shù),第一個(gè)參數(shù)為在事件觸發(fā)時(shí)真正要執(zhí)行的函數(shù)彼城,第二個(gè)參數(shù)為定義的間隔時(shí)間诅蝶,在函數(shù)執(zhí)行時(shí)定義了 lock 的初始值,通過閉包返回一個(gè)函數(shù)作為事件處理函數(shù)募壕,在返回的函數(shù)內(nèi)部判斷 lock 狀態(tài)并確定執(zhí)行真正的函數(shù) func 還是跳出调炬,每次執(zhí)行 func 后會更改 lock 狀態(tài),通過定時(shí)器在規(guī)定的時(shí)間間隔內(nèi)重置 lock舱馅,這就是函數(shù)節(jié)流的原理缰泡。

驗(yàn)證節(jié)流函數(shù)

文件:throtter-test.js

// 使用節(jié)流函數(shù)
document.addEventListener("scroll", throtter(console.log));

上面我們給 document 對象添加了滾動(dòng)事件,并不斷的打印事件對象代嗤,事件處理函數(shù)的默認(rèn)參數(shù)為事件對象棘钞,從執(zhí)行效果應(yīng)該可以看出,平均每 60ms 才會觸發(fā)一次事件干毅,達(dá)到了優(yōu)化性能的目的宜猜,如果想讓真正執(zhí)行的函數(shù) func 傳入更多的參數(shù),只需如下處理硝逢。
文件:throtter-test.js

// a b 為函數(shù)要傳入的參數(shù)
let a = 1, b = 2;

// 返回事件處理函數(shù)
const func = throtter(console.log);

// 添加事件監(jiān)聽
document.addEventListener("scroll", e => func(e, a, b));

節(jié)流函數(shù)一般用于 scroll姨拥、resize 事件的情況較多,因?yàn)檫@些事件的觸發(fā)是連續(xù)性的渠鸽,需要在一個(gè)時(shí)間間隔內(nèi)只觸發(fā)一次叫乌。

函數(shù)防抖 debounce

函數(shù)防抖:當(dāng)持續(xù)發(fā)生事件時(shí),事件只在上一次觸發(fā)后的一段時(shí)間內(nèi)沒再觸發(fā)事件拱绑,才會真正的執(zhí)行事件處理邏輯综芥,如果每兩次觸發(fā)的間隔小于這個(gè)時(shí)間,則不執(zhí)行事件邏輯猎拨。

防抖函數(shù)的時(shí)序圖

fun-debounce.png

從圖中可以看出膀藐,連續(xù)觸發(fā)事件時(shí)并沒有執(zhí)行事件處理函數(shù),只有在某一階段連續(xù)觸發(fā)后的最后一次才執(zhí)行红省,也就是上一次觸發(fā)的時(shí)間間隔要大于設(shè)定值才執(zhí)行额各,同樣的,事件處理函數(shù)內(nèi)部執(zhí)行邏輯各不相同吧恃,我們就封裝一版可通用的防抖函數(shù)虾啦。

防抖函數(shù)的封裝

文件:debounce.js

// 防抖函數(shù)
const dobounce = (func, delay = 300, timer = null) => {
    return (...args) => {
        // 清除定時(shí)器
        clearInterval(timer);

        // 在定時(shí)器到時(shí)后執(zhí)行事件處理函數(shù)
        timer = setTimeout(() => func(...args), delay);
    }
}

dobounce 函數(shù)有三個(gè)參數(shù),第一個(gè)參數(shù)為在事件觸發(fā)時(shí)真正要執(zhí)行的函數(shù),第二個(gè)參數(shù)為執(zhí)行事件的延遲時(shí)間傲醉,第三個(gè)參數(shù)為定時(shí)器 ID 的初始值蝇闭,執(zhí)行 dobounce 通過閉包返回了事件處理函數(shù),在處理函數(shù)內(nèi)部先清除定時(shí)器硬毕,然后定義定時(shí)器并將 ID 賦值給 timer呻引,如果事件連續(xù)觸發(fā),則會不斷的清除定時(shí)器吐咳,直到有一次觸發(fā)間隔超過了設(shè)定延時(shí)時(shí)間 delay逻悠,才會真正執(zhí)行 func。

驗(yàn)證防抖函數(shù)

文件:index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>函數(shù)防抖</title>
</head>
<body>
    <input type="text" id="ipt">
</body>
</html>

文件:debounce-test.js

// 使用防抖函數(shù)
let ipt = document.querySelector("#ipt");

// 添加事件監(jiān)聽
ipt.addEventListener("keyup", debounce(console.log));

上面的功能跟 throtter 類似韭脊,真正執(zhí)行事件處理函數(shù)時(shí)打印事件對象童谒,通過驗(yàn)證,連續(xù)輸入觸發(fā) keyup 事件沪羔,上一次觸發(fā)和下一次觸發(fā)間隔時(shí)間必須大于 300ms 才會執(zhí)行打印事件對象的邏輯饥伊,如果想傳入多個(gè)參數(shù)套路相同。

文件:debounce-test.js

// 獲取 dom 元素
let ipt = document.querySelector("#ipt");

// a b 為函數(shù)要傳入的參數(shù)
let a = 1, b = 2;

// 返回事件處理函數(shù)
const func = debounce(console.log);

// 添加事件監(jiān)聽
ipt.addEventListener("keyup", e => func(e, a, b));

防抖函數(shù)一般用于輸入框事件蔫饰,常用場景就是搜索或查詢撵渡,如果不使用防抖會連續(xù)發(fā)送請求,增加服務(wù)器的壓力死嗦,使用防抖后,會在用戶輸入要查詢的關(guān)鍵詞后才發(fā)送請求粒氧,這也更符合用戶的習(xí)慣越除,例如百度搜索,就是這樣實(shí)現(xiàn)的外盯。

總結(jié)

“節(jié)流” 和 “防抖” 是前端在項(xiàng)目中經(jīng)常使用的優(yōu)化手段摘盆,代碼雖然不多,但是確是前端面試 “出鏡率” 非常高的知識點(diǎn)饱苟,從而能看出它們的重要性孩擂,所以建議前端同學(xué)們一定要知道,并能手寫箱熬,這是 “一箭雙雕” 的事类垦,可以用來通過面試,也可以因?yàn)楣ぷ髦杏龅街苯泳蛯懚岣吖ぷ餍省?/p>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末城须,一起剝皮案震驚了整個(gè)濱河市蚤认,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌糕伐,老刑警劉巖砰琢,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡陪汽,警方通過查閱死者的電腦和手機(jī)训唱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挚冤,“玉大人况增,你說我怎么就攤上這事∧憷保” “怎么了巡通?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長舍哄。 經(jīng)常有香客問我宴凉,道長,這世上最難降的妖魔是什么表悬? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任弥锄,我火速辦了婚禮,結(jié)果婚禮上蟆沫,老公的妹妹穿的比我還像新娘籽暇。我一直安慰自己,他們只是感情好饭庞,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布戒悠。 她就那樣靜靜地躺著,像睡著了一般舟山。 火紅的嫁衣襯著肌膚如雪绸狐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天累盗,我揣著相機(jī)與錄音寒矿,去河邊找鬼。 笑死若债,一個(gè)胖子當(dāng)著我的面吹牛符相,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蠢琳,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼啊终,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了傲须?” 一聲冷哼從身側(cè)響起孕索,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎躏碳,沒想到半個(gè)月后搞旭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體散怖,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年肄渗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了镇眷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡翎嫡,死狀恐怖欠动,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情惑申,我是刑警寧澤具伍,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站圈驼,受9級特大地震影響人芽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜绩脆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一萤厅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧靴迫,春花似錦惕味、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至主守,卻和暖如春躺同,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背丸逸。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留剃袍,地道東北人黄刚。 一個(gè)月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像民效,于是被迫代替她去往敵國和親憔维。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345

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

  • 前言 最近和前端的小伙伴們畏邢,在討論面試題的時(shí)候业扒。談到了函數(shù)防抖和函數(shù)節(jié)流的應(yīng)用場景和原理。于是舒萎,想深入研究一下兩者...
    youthcity閱讀 23,508評論 5 78
  • 函數(shù)節(jié)流場景 例如:實(shí)現(xiàn)一個(gè)原生的拖拽功能(如果不用H5 Drag和Drop API),我們就需要一路監(jiān)聽mous...
    凡凡的小web閱讀 767評論 0 0
  • 其實(shí)早就聽說了節(jié)流和防抖的解決方案程储。這兩個(gè)的目的都是為了提升頁面中監(jiān)聽事件的性能。才疏學(xué)淺,如果有錯(cuò)誤章鲤,也希望各位...
    Katherine的小世界閱讀 1,878評論 0 6
  • 我刪著mp3里的歌摊灭,有點(diǎn)不舍,但就像所有的東西都會消逝败徊,它們也不例外≈愫簦現(xiàn)在是離開的時(shí)候了。它們會成為回憶皱蹦,假若有一...
    John_Yu閱讀 146評論 0 0
  • 在20多歲煤杀,剛工作1年左右的時(shí)候,大家都會比較迷茫沪哺。不是一個(gè)沒有經(jīng)驗(yàn)的人沈自。對于工作,慢慢的察覺到自己的喜好凤粗,對現(xiàn)在...
    夢回一夏閱讀 933評論 4 4