中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么在javascript中使用debounce防抖函數

發布時間:2021-05-14 16:13:48 來源:億速云 閱讀:458 作者:Leah 欄目:web開發

這期內容當中小編將會給大家帶來有關怎么在javascript中使用debounce防抖函數,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

定義及解讀

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

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

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

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

怎么在javascript中使用debounce防抖函數

原理及實現

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

實現 1

// 實現 1
// fn 是需要防抖處理的函數
// wait 是時間間隔
function debounce(fn, wait = 50) {
// 通過閉包緩存一個定時器 id
let timer = null
// 將 debounce 處理結果當作函數返回
// 觸發事件回調時執行這個返回函數
return function(...args) {
// 如果已經設定過定時器就清空上一次的定時器
if (timer) clearTimeout(timer)
// 開始設定一個新的定時器,定時器結束后執行傳入的函數 fn
timer = 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 = null
return 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 秒后才執行函數 fn
document.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 設置為當前時間并執行函數 fn
if (now - previous > wait) {
previous = now
fn.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 = now
fn.apply(this, args)
}, wait)
// ------ 新增部分 end ------ 
} else {
// 第一次執行
// 或者時間間隔超出了設定的時間間隔,執行函數 fn
previous = now
fn.apply(this, args)
}
}
}
// DEMO
// 執行 throttle 函數返回新函數
const betterFn = throttle(() => console.log('fn 節流執行了'), 1000)
// 第一次觸發 scroll 執行一次 fn,每隔 1 秒后執行一次函數 fn,停止滑動 1 秒后再執行函數 fn
document.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、觸發執行 func
var later = function(context, args) {
timeout = null;
// if (args) 判斷是為了過濾立即觸發的
// 關聯在于 _.delay 和 restArguments
if (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 輸入回調事件添加防抖函數后,只會在停止輸入后觸發一次

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

JavaScript有什么特點

1、js屬于一種解釋性腳本語言;2、在絕大多數瀏覽器的支持下,js可以在多種平臺下運行,擁有著跨平臺特性;3、js屬于一種弱類型腳本語言,對使用的數據類型未做出嚴格的要求,能夠進行類型轉換,簡單又容易上手;4、js語言安全性高,只能通過瀏覽器實現信息瀏覽或動態交互,從而有效地防止數據的丟失;5、基于對象的腳本語言,js不僅可以創建對象,也能使用現有的對象。

上述就是小編為大家分享的怎么在javascript中使用debounce防抖函數了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

额济纳旗| 津南区| 瓦房店市| 侯马市| 平凉市| 巴中市| 漳州市| 苗栗市| 普宁市| 宝鸡市| 永济市| 芦溪县| 灵武市| 格尔木市| 元氏县| 德安县| 泉州市| 上思县| 远安县| 新邵县| 东安县| 盐山县| 龙南县| 韶山市| 漳浦县| 承德县| 洛南县| 布尔津县| 始兴县| 郸城县| 栾城县| 田林县| 秀山| 千阳县| 滨海县| 新蔡县| 鄱阳县| 南充市| 古交市| 登封市| 鄯善县|