函數(shù)防抖與節(jié)流

函數(shù)防抖與節(jié)流

為什么要對(duì)函數(shù)進(jìn)行防抖與節(jié)流?

在進(jìn)行窗口的resize畏邢、scroll业扒、mousemove,輸入框內(nèi)容校驗(yàn)等操作時(shí)舒萎,如果這些高頻事件的處理函數(shù)調(diào)用的頻率無限制程储,會(huì)加重瀏覽器的負(fù)擔(dān),導(dǎo)致用戶體驗(yàn)非常糟糕臂寝。
此時(shí)我們可以采用debounce(防抖)和throttle(節(jié)流)的方式來減少調(diào)用頻率章鲤,同時(shí)又不影響實(shí)際效果。

防抖

函數(shù)防抖是指觸發(fā)高頻事件后n秒內(nèi)函數(shù)只會(huì)執(zhí)行一次咆贬,如果n秒內(nèi)高頻事件再次被觸發(fā)败徊,則重新計(jì)算時(shí)間
防抖的實(shí)現(xiàn)上有兩種方式:非立即執(zhí)行、立即執(zhí)行

非立即執(zhí)行版

非立即執(zhí)行版: 觸發(fā)高頻事件后過n秒執(zhí)行函數(shù)fn掏缎,如果n秒內(nèi)高頻事件再次被觸發(fā)皱蹦,則重新計(jì)算時(shí)間

function debounce1(fn, wait) {
  let timeout;
  return function () {
    if (timeout) {
      clearTimeout(timeout);
    }
    timeout = setTimeout(function () {
      fn.apply(this, arguments);
      console.log("可以再次觸發(fā)了");
    }, wait);
  }
}

立即執(zhí)行版

立即執(zhí)行版: 觸發(fā)高頻事件后立即執(zhí)行函數(shù)fn煤杀,過n秒后觸發(fā)高頻事件可以立即執(zhí)行函數(shù)fn,如果n秒內(nèi)高頻事件再次被觸發(fā)沪哺,則重新計(jì)算時(shí)間

function debounce2(fn, wait) {
  let timeout;
  return function () {
    if (timeout) {
      clearTimeout(timeout);
    }

    let callnow = !timeout;
    timeout = setTimeout(function () {
      timeout = null;
      console.log("可以再次觸發(fā)了");
    }, wait);
    if (callnow) {
      fn.apply(this, arguments);
    }
  }
}

綜合版

將兩種實(shí)現(xiàn)結(jié)合在一起可以得到綜合版的防抖怜珍。

/**
** @desc 函數(shù)防抖
** @param fn 函數(shù)
** @param wait 延遲執(zhí)行毫秒數(shù)
** @param immediate true 表立即執(zhí)行,false 表非立即執(zhí)行
*/
function debounce(fn, wait, immediate) {
  let timeout;
  return function () {
    let context = this;
    let args = arguments;

    if (timeout) {
      clearTimeout(timeout);
    }

    if (immediate) {
      let callnow = !timeout;
      timeout = setTimeout(function () {
        timeout = null;
      }, wait);
      if (callnow) {
        fn.apply(context, args);
        console.log("可以再次觸發(fā)了");
      }
    } else {
      timeout = setTimeout(function () {
        fn.apply(context, args);
        console.log("可以再次觸發(fā)了");
      }, wait);
    }
  }
}

節(jié)流

函數(shù)節(jié)流:當(dāng)連續(xù)觸發(fā)事件時(shí)凤粗,在 n 秒中只執(zhí)行一次函數(shù)酥泛。節(jié)流會(huì)稀釋函數(shù)的執(zhí)行頻率。
函數(shù)節(jié)流也有兩種實(shí)現(xiàn)方式:時(shí)間戳版嫌拣、定時(shí)器版

時(shí)間戳版

時(shí)間戳版: 在持續(xù)觸發(fā)事件的過程中柔袁,函數(shù)會(huì)立即執(zhí)行,并且每 1s 執(zhí)行一次异逐。

function throttle1(fn, wait) {
  let previous = 0;
  return function () {
    let context = this;
    let args = arguments;

    let now = new Date();
    if (now - previous > wait) {
      fn.apply(context, args);
      previous = now;
    }
  }
}

定時(shí)器版

定時(shí)器版: 在持續(xù)觸發(fā)事件的過程中捶索,函數(shù)不會(huì)立即執(zhí)行,并且每 1s 執(zhí)行一次灰瞻,在停止觸發(fā)事件后腥例,函數(shù)還會(huì)再執(zhí)行一次。

function throttle2(fn, wait) {
  let timeout;
  return function () {
    let context = this;
    let args = arguments;

    if (!timeout) {
      timeout = setTimeout(() => {
        timeout = null;
        fn.apply(context, args);
      }, wait);
    }
  }
}

綜合版

將兩種實(shí)現(xiàn)結(jié)合在一起可以得到綜合版的節(jié)流酝润。

/**
 * @desc 函數(shù)節(jié)流
 * @param fn 函數(shù)
 * @param wait 延遲執(zhí)行毫秒數(shù)
 * @param type "timestamp" 表時(shí)間戳版燎竖,"timeout" 表定時(shí)器版
 */
function throttle(fn, wait, type) {
  if (type === "timestamp") {
    var previous = 0;
  } else if (type === "timeout") {
    var timeout;
  }
  return function () {
    let context = this;
    let args = arguments;

    if (type === "timestamp") {
      let now = Date.now();
      if (now - previous > wait) {
        fn.apply(context, args);
        previous = now;
      }
    } else if (type === "timeout") {
      if (!timeout) {
        timeout = setTimeout(() => {
          timeout = null;
          fn.apply(context, args)
        }, wait)
      }
    }
  }
}

區(qū)別和適用場(chǎng)景

通過上面的介紹,我們可以得出防抖與節(jié)流的區(qū)別要销。
如果事件觸發(fā)頻繁构回,防抖中的計(jì)時(shí)會(huì)不斷重置,從而會(huì)不斷延遲了函數(shù)的執(zhí)行疏咐,而節(jié)流中的計(jì)時(shí)器不會(huì)因?yàn)槭录挠|發(fā)而重置纤掸。
注:以下應(yīng)用并不絕對(duì),只是寫了一下我認(rèn)為比較合適的解決方案便于理解防抖與節(jié)流的區(qū)別浑塞,具體應(yīng)用需要根據(jù)具體場(chǎng)景的需求確定借跪。

表單提交

在表單提交這一場(chǎng)景中,我們可以考慮使用立即執(zhí)行版的防抖酌壕。
用戶往往會(huì)因?yàn)樘峤粫r(shí)的等待而重復(fù)快速點(diǎn)擊按鈕進(jìn)行提交表單掏愁。顯然,這一過程中仅孩,表單的內(nèi)容不會(huì)發(fā)生改變托猩,我們只需要調(diào)用一次后臺(tái)接口。
使用立即執(zhí)行版的防抖之后辽慕,會(huì)在用戶第一次點(diǎn)擊按鈕時(shí)調(diào)用一次后臺(tái)接口京腥,如果用戶在延遲時(shí)間內(nèi)重復(fù)點(diǎn)擊,則不會(huì)執(zhí)行表單提交函數(shù)溅蛉,直至用戶最后一次點(diǎn)擊按鈕并等待超過設(shè)定的延遲時(shí)間之后再次點(diǎn)擊按鈕公浪,才會(huì)執(zhí)行表單提交函數(shù)他宛。

瀏覽器窗口滾動(dòng)事件

之前碰到過一個(gè)滾動(dòng)切換導(dǎo)航欄項(xiàng)的效果。
先獲取各個(gè)元素的位置offsetTop欠气,監(jiān)聽瀏覽器窗口滾動(dòng)事件厅各,觸發(fā)該事件時(shí),獲取滾動(dòng)條的位置scrollTop预柒,遍歷offsetTop與scrollTop進(jìn)行比較队塘,從而確定當(dāng)前位置需要高亮哪個(gè)導(dǎo)航欄項(xiàng)。
在這一場(chǎng)景中宜鸯,我們需要在多次事件觸發(fā)的過程中憔古,執(zhí)行多次函數(shù),因此淋袖,我們可以考慮使用時(shí)間戳版的節(jié)流(定時(shí)器版也可)鸿市。
減少函數(shù)的執(zhí)行頻率,防止因大量計(jì)算導(dǎo)致卡頓即碗。

參考文章

http://www.reibang.com/p/c8b86b09daf0

總結(jié)

本文主要對(duì)函數(shù)防抖與節(jié)流做了一些介紹焰情,提供了一些實(shí)現(xiàn)方法,并分析了防抖與節(jié)流的區(qū)別剥懒,舉例說明了適用場(chǎng)景内舟。

公眾號(hào):成的學(xué)習(xí)之路。跪求一波關(guān)注= =
在公眾號(hào)回復(fù) 防抖與節(jié)流蕊肥, 即可獲取源碼地址

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末谒获,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子壁却,更是在濱河造成了極大的恐慌,老刑警劉巖裸准,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件展东,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡炒俱,警方通過查閱死者的電腦和手機(jī)盐肃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門砸王,熙熙樓的掌柜王于貴愁眉苦臉地迎上來峦阁,“玉大人,你說我怎么就攤上這事驹闰”窬” “怎么了师妙?”我有些...
    開封第一講書人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵默穴,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我蓄诽,道長(zhǎng)溜歪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任调衰,我火速辦了婚禮嚎莉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘趋箩。我一直安慰自己叫确,他們只是感情好芍锦,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著次乓,像睡著了一般孽水。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上杏慰,一...
    開封第一講書人閱讀 51,155評(píng)論 1 299
  • 那天逃默,我揣著相機(jī)與錄音,去河邊找鬼完域。 笑死,一個(gè)胖子當(dāng)著我的面吹牛凹耙,可吹牛的內(nèi)容都是我干的肠仪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼意述,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼荤崇!你這毒婦竟也來了潮针?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤瓣戚,失蹤者是張志新(化名)和其女友劉穎焦读,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體刚照,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年啊楚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拯辙。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡涯保,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出夕春,到底是詐尸還是另有隱情,我是刑警寧澤片排,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布速侈,位于F島的核電站,受9級(jí)特大地震影響冶共,放射性物質(zhì)發(fā)生泄漏每界。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一命咐、第九天 我趴在偏房一處隱蔽的房頂上張望谐岁。 院中可真熱鬧,春花似錦窜司、人聲如沸航揉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽斯议。三九已至醇锚,卻和暖如春坯临,著一層夾襖步出監(jiān)牢的瞬間恋昼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來泰國打工挟炬, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辟宗,地道東北人吝秕。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像容客,于是被迫代替她去往敵國和親约郁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

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