真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

怎么在javascript中使用debounce防抖函數(shù)

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)碛嘘P(guān)怎么在javascript中使用debounce防抖函數(shù),文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

成都創(chuàng)新互聯(lián)公司專注于中大型企業(yè)的網(wǎng)站建設(shè)、網(wǎng)站設(shè)計(jì)和網(wǎng)站改版、網(wǎng)站營銷服務(wù),追求商業(yè)策劃與數(shù)據(jù)分析、創(chuàng)意藝術(shù)與技術(shù)開發(fā)的融合,累計(jì)客戶成百上千,服務(wù)滿意度達(dá)97%。幫助廣大客戶順利對(duì)接上互聯(lián)網(wǎng)浪潮,準(zhǔn)確優(yōu)選出符合自己需要的互聯(lián)網(wǎng)運(yùn)用,我們將一直專注成都品牌網(wǎng)站建設(shè)和互聯(lián)網(wǎng)程序開發(fā),在前進(jìn)的路上,與客戶一起成長!

定義及解讀

防抖函數(shù) debounce 指的是某個(gè)函數(shù)在某段時(shí)間內(nèi),無論觸發(fā)了多少次回調(diào),都只執(zhí)行最后一次。假如我們?cè)O(shè)置了一個(gè)等待時(shí)間 3 秒的函數(shù),在這 3 秒內(nèi)如果遇到函數(shù)調(diào)用請(qǐng)求就重新計(jì)時(shí) 3 秒,直至新的 3 秒內(nèi)沒有函數(shù)調(diào)用請(qǐng)求,此時(shí)執(zhí)行函數(shù),不然就以此類推重新計(jì)時(shí)。

舉一個(gè)小例子:假定在做公交車時(shí),司機(jī)需等待最后一個(gè)人進(jìn)入后再關(guān)門,每次新進(jìn)一個(gè)人,司機(jī)就會(huì)把計(jì)時(shí)器清零并重新開始計(jì)時(shí),重新等待 1 分鐘再關(guān)門,如果后續(xù) 1 分鐘內(nèi)都沒有乘客上車,司機(jī)會(huì)認(rèn)為乘客都上來了,將關(guān)門發(fā)車。

此時(shí)「上車的乘客」就是我們頻繁操作事件而不斷涌入的回調(diào)任務(wù);「1 分鐘」就是計(jì)時(shí)器,它是司機(jī)決定「關(guān)門」的依據(jù),如果有新的「乘客」上車,將清零并重新計(jì)時(shí);「關(guān)門」就是最后需要執(zhí)行的函數(shù)。

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

怎么在javascript中使用debounce防抖函數(shù)

原理及實(shí)現(xiàn)

實(shí)現(xiàn)原理就是利用定時(shí)器,函數(shù)第一次執(zhí)行時(shí)設(shè)定一個(gè)定時(shí)器,之后調(diào)用時(shí)發(fā)現(xiàn)已經(jīng)設(shè)定過定時(shí)器就清空之前的定時(shí)器,并重新設(shè)定一個(gè)新的定時(shí)器,如果存在沒有被清空的定時(shí)器,當(dāng)定時(shí)器計(jì)時(shí)結(jié)束后觸發(fā)函數(shù)執(zhí)行。

實(shí)現(xiàn) 1

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

實(shí)現(xiàn) 2

上述實(shí)現(xiàn)方案已經(jīng)可以解決大部分使用場景了,不過想要實(shí)現(xiàn)第一次觸發(fā)回調(diào)事件就執(zhí)行 fn 有點(diǎn)力不從心了,這時(shí)候我們來改寫下 debounce 函數(shù),加上第一次觸發(fā)立即執(zhí)行的功能。

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

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

加強(qiáng)版 throttle

現(xiàn)在考慮一種情況,如果用戶的操作非常頻繁,不等設(shè)置的延遲時(shí)間結(jié)束就進(jìn)行下次操作,會(huì)頻繁的清除計(jì)時(shí)器并重新生成,所以函數(shù) fn 一直都沒辦法執(zhí)行,導(dǎo)致用戶操作遲遲得不到響應(yīng)。

有一種思想是將「節(jié)流」和「防抖」合二為一,變成加強(qiáng)版的節(jié)流函數(shù),關(guān)鍵點(diǎn)在于「 wait 時(shí)間內(nèi),可以重新生成定時(shí)器,但只要 wait 的時(shí)間到了,必須給用戶一個(gè)響應(yīng)」。這種合體思路恰好可以解決上面提出的問題。

給出合二為一的代碼之前先來回顧下 throttle 函數(shù),上一小節(jié)中有詳細(xì)的介紹。

// fn 是需要執(zhí)行的函數(shù)
// wait 是時(shí)間間隔
const throttle = (fn, wait = 50) => {
// 上一次執(zhí)行 fn 的時(shí)間
let previous = 0
// 將 throttle 處理結(jié)果當(dāng)作函數(shù)返回
return function(...args) {
// 獲取當(dāng)前時(shí)間,轉(zhuǎn)換成時(shí)間戳,單位毫秒
let now = +new Date()
// 將當(dāng)前時(shí)間和上一次執(zhí)行函數(shù)的時(shí)間進(jìn)行對(duì)比
// 大于等待時(shí)間就把 previous 設(shè)置為當(dāng)前時(shí)間并執(zhí)行函數(shù) fn
if (now - previous > wait) {
previous = now
fn.apply(this, args)
}
}
}

結(jié)合 throttle 和 debounce 代碼,加強(qiáng)版節(jié)流函數(shù) throttle 如下,新增邏輯在于當(dāng)前觸發(fā)時(shí)間和上次觸發(fā)的時(shí)間差小于時(shí)間間隔時(shí),設(shè)立一個(gè)新的定時(shí)器,相當(dāng)于把 debounce 代碼放在了小于時(shí)間間隔部分。

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

看完整段代碼會(huì)發(fā)現(xiàn)這個(gè)思想和上篇文章介紹的 underscore 中 throttle 的實(shí)現(xiàn)思想非常相似。

underscore 源碼解析

看完了上文的基本版代碼,感覺還是比較輕松的,現(xiàn)在來學(xué)習(xí)下 underscore 是如何實(shí)現(xiàn) debounce 函數(shù)的,學(xué)習(xí)一下優(yōu)秀的思想,直接上代碼和注釋,本源碼解析依賴于 underscore 1.9.1 版本實(shí)現(xiàn)。

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

相比上文的基本版實(shí)現(xiàn),underscore 多了以下幾點(diǎn)功能。

1、函數(shù) func 的執(zhí)行結(jié)束后返回結(jié)果值 result

2、定時(shí)器計(jì)時(shí)結(jié)束后清除 timeout,使之不影響下次連續(xù)事件的觸發(fā)

3、新增了手動(dòng)取消功能 cancel

4、immediate 為 true 后只會(huì)在第一次觸發(fā)時(shí)執(zhí)行,頻繁觸發(fā)回調(diào)結(jié)束后不會(huì)再執(zhí)行

小結(jié)

  • 函數(shù)節(jié)流和防抖都是「閉包」、「高階函數(shù)」的應(yīng)用

  • 函數(shù)節(jié)流 throttle 指的是某個(gè)函數(shù)在一定時(shí)間間隔內(nèi)(例如 3 秒)執(zhí)行一次,在這 3 秒內(nèi) 無視后來產(chǎn)生的函數(shù)調(diào)用請(qǐng)求

    • 「管道中的水」就是我們頻繁操作事件而不斷涌入的回調(diào)任務(wù),它需要接受「水龍頭」安排

    • 「水龍頭」就是節(jié)流閥,控制水的流速,過濾無效的回調(diào)任務(wù)

    • 「滴水」就是每隔一段時(shí)間執(zhí)行一次函數(shù)

    • 「3 秒」就是間隔時(shí)間,它是「水龍頭」決定「滴水」的依據(jù)

    • 節(jié)流可以理解為養(yǎng)金魚時(shí)擰緊水龍頭放水,3 秒一滴

    • 應(yīng)用:監(jiān)聽滾動(dòng)事件添加節(jié)流函數(shù)后,每隔固定的一段時(shí)間執(zhí)行一次

    • 實(shí)現(xiàn)方案 1:用時(shí)間戳來判斷是否已到執(zhí)行時(shí)間,記錄上次執(zhí)行的時(shí)間戳,然后每次觸發(fā)后執(zhí)行回調(diào),判斷當(dāng)前時(shí)間距離上次執(zhí)行時(shí)間的間隔是否已經(jīng)達(dá)到時(shí)間差(Xms) ,如果是則執(zhí)行,并更新上次執(zhí)行的時(shí)間戳,如此循環(huán)

    • 實(shí)現(xiàn)方案 2:使用定時(shí)器,比如當(dāng) scroll 事件剛觸發(fā)時(shí),打印一個(gè) hello world,然后設(shè)置個(gè) 1000ms 的定時(shí)器,此后每次觸發(fā) scroll 事件觸發(fā)回調(diào),如果已經(jīng)存在定時(shí)器,則回調(diào)不執(zhí)行方法,直到定時(shí)器觸發(fā),handler 被清除,然后重新設(shè)置定時(shí)器

  • 函數(shù)防抖 debounce 指的是某個(gè)函數(shù)在某段時(shí)間內(nèi),無論觸發(fā)了多少次回調(diào),都只執(zhí)行最后一次

    • 「上車的乘客」就是我們頻繁操作事件而不斷涌入的回調(diào)任務(wù)

    • 「1 分鐘」就是計(jì)時(shí)器,它是司機(jī)決定「關(guān)門」的依據(jù),如果有新的「乘客」上車,將清零并重新計(jì)時(shí)

    • 「關(guān)門」就是最后需要執(zhí)行的函數(shù)

    • 防抖可以理解為司機(jī)等待最后一個(gè)人進(jìn)入后再關(guān)門,每次新進(jìn)一個(gè)人,司機(jī)就會(huì)把計(jì)時(shí)器清零并重新開始計(jì)時(shí)

    • 應(yīng)用:input 輸入回調(diào)事件添加防抖函數(shù)后,只會(huì)在停止輸入后觸發(fā)一次

    • 實(shí)現(xiàn)方案:使用定時(shí)器,函數(shù)第一次執(zhí)行時(shí)設(shè)定一個(gè)定時(shí)器,之后調(diào)用時(shí)發(fā)現(xiàn)已經(jīng)設(shè)定過定時(shí)器就清空之前的定時(shí)器,并重新設(shè)定一個(gè)新的定時(shí)器,如果存在沒有被清空的定時(shí)器,當(dāng)定時(shí)器計(jì)時(shí)結(jié)束后觸發(fā)函數(shù)執(zhí)行

JavaScript有什么特點(diǎn)

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

上述就是小編為大家分享的怎么在javascript中使用debounce防抖函數(shù)了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。


分享文章:怎么在javascript中使用debounce防抖函數(shù)
網(wǎng)頁URL:http://weahome.cn/article/pgsojp.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部