亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > JS > 正文

javascript防抖函數debounce詳解

2024-05-06 16:52:02
字體:
來源:轉載
供稿:網友

定義及解讀

防抖函數 debounce 指的是某個函數在某段時間內,無論觸發了多少次回調,都只執行最后一次。假如我們設置了一個等待時間 3 秒的函數,在這 3 秒內如果遇到函數調用請求就重新計時 3 秒,直至新的 3 秒內沒有函數調用請求,此時執行函數,不然就以此類推重新計時。

舉一個小例子:假定在做公交車時,司機需等待最后一個人進入后再關門,每次新進一個人,司機就會把計時器清零并重新開始計時,重新等待 1 分鐘再關門,如果后續 1 分鐘內都沒有乘客上車,司機會認為乘客都上來了,將關門發車。

此時「上車的乘客」就是我們頻繁操作事件而不斷涌入的回調任務;「1 分鐘」就是計時器,它是司機決定「關門」的依據,如果有新的「乘客」上車,將清零并重新計時;「關門」就是最后需要執行的函數。

如果你還無法理解,看下面這張圖就清晰多了,另外點擊 這個頁面 查看節流和防抖的可視化比較。其中 Regular 是不做任何處理的情況,throttle 是函數節流之后的結果(上一小節已介紹),debounce 是函數防抖之后的結果。

javascript,防抖函數,debounce

原理及實現

實現原理就是利用定時器,函數第一次執行時設定一個定時器,之后調用時發現已經設定過定時器就清空之前的定時器,并重新設定一個新的定時器,如果存在沒有被清空的定時器,當定時器計時結束后觸發函數執行。

實現 1

// 實現 1// fn 是需要防抖處理的函數// wait 是時間間隔function debounce(fn, wait = 50) {// 通過閉包緩存一個定時器 idlet timer = null// 將 debounce 處理結果當作函數返回// 觸發事件回調時執行這個返回函數return function(...args) {// 如果已經設定過定時器就清空上一次的定時器if (timer) clearTimeout(timer)// 開始設定一個新的定時器,定時器結束后執行傳入的函數 fntimer = setTimeout(() => {fn.apply(this, args)}, wait)}}// DEMO// 執行 debounce 函數返回新函數const betterFn = debounce(() => console.log('fn 防抖執行了'), 1000)// 停止滑動 1 秒后執行函數 () => console.log('fn 防抖執行了')document.addEventListener('scroll', betterFn)

實現 2

上述實現方案已經可以解決大部分使用場景了,不過想要實現第一次觸發回調事件就執行 fn 有點力不從心了,這時候我們來改寫下 debounce 函數,加上第一次觸發立即執行的功能。

// 實現 2// immediate 表示第一次是否立即執行function debounce(fn, wait = 50, immediate) {let timer = nullreturn function(...args) {if (timer) clearTimeout(timer)// ------ 新增部分 start ------ // immediate 為 true 表示第一次觸發后執行// timer 為空表示首次觸發if (immediate && !timer) {fn.apply(this, args)}// ------ 新增部分 end ------ timer = setTimeout(() => {fn.apply(this, args)}, wait)}}// DEMO// 執行 debounce 函數返回新函數const betterFn = debounce(() => console.log('fn 防抖執行了'), 1000, true)// 第一次觸發 scroll 執行一次 fn,后續只有在停止滑動 1 秒后才執行函數 fndocument.addEventListener('scroll', betterFn)

實現原理比較簡單,判斷傳入的 immediate 是否為 true,另外需要額外判斷是否是第一次執行防抖函數,判斷依舊就是 timer 是否為空,所以只要 immediate && !timer 返回 true 就執行 fn 函數,即 fn.apply(this, args)。

加強版 throttle

現在考慮一種情況,如果用戶的操作非常頻繁,不等設置的延遲時間結束就進行下次操作,會頻繁的清除計時器并重新生成,所以函數 fn 一直都沒辦法執行,導致用戶操作遲遲得不到響應。

有一種思想是將「節流」和「防抖」合二為一,變成加強版的節流函數,關鍵點在于「 wait 時間內,可以重新生成定時器,但只要 wait 的時間到了,必須給用戶一個響應」。這種合體思路恰好可以解決上面提出的問題。

給出合二為一的代碼之前先來回顧下 throttle 函數,上一小節中有詳細的介紹。

// fn 是需要執行的函數// wait 是時間間隔const throttle = (fn, wait = 50) => {// 上一次執行 fn 的時間let previous = 0// 將 throttle 處理結果當作函數返回return function(...args) {// 獲取當前時間,轉換成時間戳,單位毫秒let now = +new Date()// 將當前時間和上一次執行函數的時間進行對比// 大于等待時間就把 previous 設置為當前時間并執行函數 fnif (now - previous > wait) {previous = nowfn.apply(this, args)}}}

結合 throttle 和 debounce 代碼,加強版節流函數 throttle 如下,新增邏輯在于當前觸發時間和上次觸發的時間差小于時間間隔時,設立一個新的定時器,相當于把 debounce 代碼放在了小于時間間隔部分。

// fn 是需要節流處理的函數// wait 是時間間隔function throttle(fn, wait) {// previous 是上一次執行 fn 的時間// timer 是定時器let previous = 0, timer = null// 將 throttle 處理結果當作函數返回return function (...args) {// 獲取當前時間,轉換成時間戳,單位毫秒let now = +new Date()// ------ 新增部分 start ------ // 判斷上次觸發的時間和本次觸發的時間差是否小于時間間隔if (now - previous < wait) {// 如果小于,則為本次觸發操作設立一個新的定時器// 定時器時間結束后執行函數 fn if (timer) clearTimeout(timer)timer = setTimeout(() => {previous = nowfn.apply(this, args)}, wait)// ------ 新增部分 end ------ } else {// 第一次執行// 或者時間間隔超出了設定的時間間隔,執行函數 fnprevious = nowfn.apply(this, args)}}}// DEMO// 執行 throttle 函數返回新函數const betterFn = throttle(() => console.log('fn 節流執行了'), 1000)// 第一次觸發 scroll 執行一次 fn,每隔 1 秒后執行一次函數 fn,停止滑動 1 秒后再執行函數 fndocument.addEventListener('scroll', betterFn)

看完整段代碼會發現這個思想和上篇文章介紹的 underscore 中 throttle 的實現思想非常相似。

underscore 源碼解析

看完了上文的基本版代碼,感覺還是比較輕松的,現在來學習下 underscore 是如何實現 debounce 函數的,學習一下優秀的思想,直接上代碼和注釋,本源碼解析依賴于 underscore 1.9.1 版本實現。

// 此處的三個參數上文都有解釋_.debounce = function(func, wait, immediate) {// timeout 表示定時器// result 表示 func 執行返回值var timeout, result;// 定時器計時結束后// 1、清空計時器,使之不影響下次連續事件的觸發// 2、觸發執行 funcvar later = function(context, args) {timeout = null;// if (args) 判斷是為了過濾立即觸發的// 關聯在于 _.delay 和 restArgumentsif (args) result = func.apply(context, args);};// 將 debounce 處理結果當作函數返回var debounced = restArguments(function(args) {if (timeout) clearTimeout(timeout);if (immediate) {// 第一次觸發后會設置 timeout,// 根據 timeout 是否為空可以判斷是否是首次觸發var callNow = !timeout;timeout = setTimeout(later, wait);if (callNow) result = func.apply(this, args);} else {// 設置定時器timeout = _.delay(later, wait, this, args);}return result;});// 新增 手動取消debounced.cancel = function() {clearTimeout(timeout);timeout = null;};return debounced;};// 根據給定的毫秒 wait 延遲執行函數 func_.delay = restArguments(function(func, wait, args) {return setTimeout(function() {return func.apply(null, args);}, wait);});

相比上文的基本版實現,underscore 多了以下幾點功能。

1、函數 func 的執行結束后返回結果值 result

2、定時器計時結束后清除 timeout,使之不影響下次連續事件的觸發

3、新增了手動取消功能 cancel

4、immediate 為 true 后只會在第一次觸發時執行,頻繁觸發回調結束后不會再執行

小結

  • 函數節流和防抖都是「閉包」、「高階函數」的應用
  • 函數節流 throttle 指的是某個函數在一定時間間隔內(例如 3 秒)執行一次,在這 3 秒內 無視后來產生的函數調用請求
    • 節流可以理解為養金魚時擰緊水龍頭放水,3 秒一滴
      • 「管道中的水」就是我們頻繁操作事件而不斷涌入的回調任務,它需要接受「水龍頭」安排
      • 「水龍頭」就是節流閥,控制水的流速,過濾無效的回調任務
      • 「滴水」就是每隔一段時間執行一次函數
      • 「3 秒」就是間隔時間,它是「水龍頭」決定「滴水」的依據
    • 應用:監聽滾動事件添加節流函數后,每隔固定的一段時間執行一次
    • 實現方案 1:用時間戳來判斷是否已到執行時間,記錄上次執行的時間戳,然后每次觸發后執行回調,判斷當前時間距離上次執行時間的間隔是否已經達到時間差(Xms) ,如果是則執行,并更新上次執行的時間戳,如此循環
    • 實現方案 2:使用定時器,比如當 scroll 事件剛觸發時,打印一個 hello world,然后設置個 1000ms 的定時器,此后每次觸發 scroll 事件觸發回調,如果已經存在定時器,則回調不執行方法,直到定時器觸發,handler 被清除,然后重新設置定時器
  • 函數防抖 debounce 指的是某個函數在某段時間內,無論觸發了多少次回調,都只執行最后一次
    • 防抖可以理解為司機等待最后一個人進入后再關門,每次新進一個人,司機就會把計時器清零并重新開始計時
      • 「上車的乘客」就是我們頻繁操作事件而不斷涌入的回調任務
      • 「1 分鐘」就是計時器,它是司機決定「關門」的依據,如果有新的「乘客」上車,將清零并重新計時
      • 「關門」就是最后需要執行的函數
    • 應用:input 輸入回調事件添加防抖函數后,只會在停止輸入后觸發一次
    • 實現方案:使用定時器,函數第一次執行時設定一個定時器,之后調用時發現已經設定過定時器就清空之前的定時器,并重新設定一個新的定時器,如果存在沒有被清空的定時器,當定時器計時結束后觸發函數執行

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。


注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩禁在线播放| 91中文精品字幕在线视频| 欧美日韩福利在线观看| 欧美亚洲一级片| 欧美插天视频在线播放| 亚洲综合最新在线| 欧美一级在线亚洲天堂| 欧美野外猛男的大粗鳮| 国产成人精品av在线| 国产亚洲精品91在线| 亚洲国产99精品国自产| 国产精品女主播视频| 日韩中文字幕在线视频播放| 亚洲欧美国产制服动漫| 成人黄色生活片| 久久久久久久成人| 在线观看国产欧美| 久久99久久99精品中文字幕| 久久久久久久久久国产| 国内精品美女av在线播放| 久久av红桃一区二区小说| 欧美日韩国产综合新一区| 欧美剧在线观看| 成人午夜在线视频一区| 欧美亚洲激情在线| 成人黄色免费网站在线观看| 亚洲美女av黄| 欧美成人精品在线观看| 国产精品91久久久久久| 91人人爽人人爽人人精88v| 中文日韩电影网站| 国产精品一区二区久久精品| 国产精品视频内| 亚洲一区二区少妇| 日韩av网址在线观看| 国产伦精品一区二区三区精品视频| 成人性生交xxxxx网站| 亚洲精品av在线播放| 亚洲欧美制服第一页| 57pao成人永久免费视频| 日韩小视频网址| 欧美日韩国产精品一区二区不卡中文| 精品国产一区二区三区久久狼黑人| 中文字幕日韩在线播放| 久久久久久成人精品| 国产精品视频xxxx| 777国产偷窥盗摄精品视频| 国产午夜精品免费一区二区三区| 中文字幕欧美国内| 国产精品第一视频| 日韩国产精品一区| 亚洲欧美在线免费| 在线观看国产精品淫| 亚洲丁香久久久| 色综合亚洲精品激情狠狠| 亚洲国产精品女人久久久| 国产精品大片wwwwww| 中文字幕精品影院| 91亚洲精品一区二区| 日韩大片免费观看视频播放| 亚洲男人天堂手机在线| 91丨九色丨国产在线| 亚洲国产黄色片| 精品久久久久久久大神国产| 亚洲va电影大全| 一区二区三区www| 国产99久久精品一区二区| 奇米四色中文综合久久| 日韩欧美精品在线观看| 亚洲最大福利视频网站| 欧美老少配视频| 成人免费大片黄在线播放| 日韩在线观看av| 91极品视频在线| 国产视频在线一区二区| 九九热视频这里只有精品| 精品福利在线观看| 国产精品日韩精品| 久久久久久国产| 国产偷国产偷亚洲清高网站| 狠狠躁夜夜躁久久躁别揉| 亚洲第一精品福利| 2019日本中文字幕| 国模叶桐国产精品一区| 日韩三级成人av网| 国产精品视频自拍| 欧美视频专区一二在线观看| 国产精品日韩电影| 国产精品亚洲美女av网站| 日韩欧美在线观看| 国产日韩欧美91| 91麻豆国产精品| 日韩av在线精品| 日韩精品在线视频观看| 日韩最新中文字幕电影免费看| 国产精品欧美日韩| 国产这里只有精品| 国产欧美欧洲在线观看| 亚洲自拍偷拍色图| 欧美二区在线播放| 亚洲视频在线观看网站| 色午夜这里只有精品| 92版电视剧仙鹤神针在线观看| 亚洲精品中文字幕女同| 日本亚洲欧洲色α| 欧美日本在线视频中文字字幕| 欧美在线www| 亚洲欧美日韩爽爽影院| 韩国19禁主播vip福利视频| 欧美日韩在线看| 黑人精品xxx一区一二区| xvideos亚洲| 欧美亚洲午夜视频在线观看| 91亚洲精品视频| 欧美激情a在线| 日韩va亚洲va欧洲va国产| 在线电影中文日韩| 国产成人av网| 亚洲免费视频观看| 97在线视频精品| 一区二区三区视频免费| 亚洲另类图片色| 亚洲第一区第一页| 精品亚洲国产成av人片传媒| 亚洲午夜女主播在线直播| 亚洲bt欧美bt日本bt| 亚洲亚裔videos黑人hd| 美女撒尿一区二区三区| 国产精品天天狠天天看| 久久婷婷国产麻豆91天堂| 亚洲天堂免费在线| 亚洲精品美女网站| 俺也去精品视频在线观看| 久久伊人精品一区二区三区| 欧美激情一级精品国产| 久久精品国产成人| 精品久久久久久国产91| 久久精品福利视频| 91亚洲精品在线观看| 亚洲精品日韩激情在线电影| 国产99视频在线观看| 日韩免费在线视频| 亚洲偷欧美偷国内偷| 91亚洲人电影| 精品国产欧美一区二区三区成人| 亚洲福利在线播放| 5252色成人免费视频| 青青草成人在线| 亚洲成人av在线播放| 欧美丰满少妇xxxxx| 亚洲国产天堂久久综合网| 91精品久久久久久久久青青| 亚洲精品视频免费在线观看| 久久久久久成人| 国产性猛交xxxx免费看久久| 成人中心免费视频| 欧美激情2020午夜免费观看| 国产精品久久久久7777婷婷| 成人激情视频小说免费下载| 国产大片精品免费永久看nba| 国产精品永久免费| 粉嫩老牛aⅴ一区二区三区| 国产精品久久久久久网站| 欧美成人h版在线观看|