概念
函數(shù)防抖(debounce)
當(dāng)調(diào)用動作過n毫秒后皆愉,才會執(zhí)行該動作嚷炉,若在這n毫秒內(nèi)又調(diào)用此動作則將重新計(jì)算執(zhí)行時間
函數(shù)節(jié)流(throttle)
預(yù)先設(shè)定一個執(zhí)行周期舞蔽,當(dāng)調(diào)用動作的時刻大于等于執(zhí)行周期則執(zhí)行該動作租副,然后進(jìn)入下一個新周期
函數(shù)節(jié)流(throttle)與函數(shù)防抖(debounce)都是為了限制函數(shù)的執(zhí)行頻次抗愁,以優(yōu)化函數(shù)觸發(fā)頻率過高導(dǎo)致的響應(yīng)速度跟不上觸發(fā)頻率郑藏,出現(xiàn)延遲衡查,假死或卡頓的現(xiàn)象。
比如如下的情況:
1. window對象的resize(用于把窗口大小調(diào)整為指定的寬度和高度)必盖、scroll(把內(nèi)容滾動指定的像素?cái)?shù))事件
2. 拖拽時的mousemove事件
3. 文字輸入拌牲、自動完成的keyup事件
區(qū)別
可以拿我們平時坐電梯為例來形象地表述二者的區(qū)別
函數(shù)防抖:如果有人進(jìn)電梯(觸發(fā)事件),那電梯將在10秒鐘后出發(fā)(執(zhí)行事件監(jiān)聽器)歌粥,這時如果又有人進(jìn)電梯了(在10秒內(nèi)再次觸發(fā)該事件)们拙,我們又得等10秒再出發(fā)(重新計(jì)時)。
函數(shù)節(jié)流:保證如果電梯第一個人進(jìn)來后阁吝,10秒后準(zhǔn)時運(yùn)送一次砚婆,這個時間從第一個人上電梯開始計(jì)時,不等待,如果沒有人装盯,則不運(yùn)行
實(shí)現(xiàn)
函數(shù)節(jié)流應(yīng)用的實(shí)際場景坷虑,多數(shù)在監(jiān)聽頁面元素滾動事件的時候會用到。因?yàn)闈L動事件埂奈,是一個高頻觸發(fā)的事件迄损。以下是監(jiān)聽頁面元素滾動的示例代碼:
// 函數(shù)節(jié)流
var? canRun =true;
document.getElementById("throttle").onscroll =function(){
if(!canRun){
?????? // 判斷是否已空閑,如果在執(zhí)行中账磺,則直接return
return;? ?
}? ?
canRun =false;? ?
setTimeout(function(){
???? console.log("函數(shù)節(jié)流");? ? ? ?
????? canRun =true;?
? },300);
};
函數(shù)節(jié)流的要點(diǎn)是芹敌,聲明一個變量當(dāng)標(biāo)志位,記錄當(dāng)前代碼是否在執(zhí)行垮抗。
如果空閑氏捞,則可以正常觸發(fā)方法執(zhí)行。
如果代碼正在執(zhí)行,則取消這次方法執(zhí)行,直接return太防。
這個方法的作用是監(jiān)聽ID為throttle元素的滾動事件。
當(dāng)canRun為true捆等,則代表現(xiàn)在的滾動處理事件是空閑的,可以使用续室。
通過關(guān)卡if(!canRun)栋烤,等于就拿到了通行證。然后下一步的操作就是立馬將關(guān)卡關(guān)上canRun=false挺狰。這樣班缎,其他請求執(zhí)行滾動事件的方法,就被擋回去了她渴。
接著用setTimeout規(guī)定最小的時間間隔300达址,接著再執(zhí)行setTimeout方法體里面的內(nèi)容。
最后趁耗,等setTimeout里面的方法都執(zhí)行完畢沉唠,才釋放關(guān)卡canRun=true,允許下一個訪問者進(jìn)來苛败。
這個函數(shù)節(jié)流的實(shí)現(xiàn)形式满葛,需要注意的是執(zhí)行的間隔時間是>=300ms。如果具體執(zhí)行的方法是包含callback的罢屈,也可以將canRun=true這一步放到callback中嘀韧。理解了函數(shù)節(jié)流的關(guān)卡設(shè)置重點(diǎn),其實(shí)改起來就簡單多了缠捌。
三锄贷、函數(shù)防抖
?函數(shù)防抖的應(yīng)用場景译蒂,最常見的就是用戶注冊時候的手機(jī)號碼驗(yàn)證和郵箱驗(yàn)證了。只有等用戶輸入完畢后谊却,前端才需要檢查格式是否正確柔昼,如果不正確,再彈出提示語炎辨。以下還是以頁面元素滾動監(jiān)聽的例子捕透,來進(jìn)行解析:
// 函數(shù)防抖
var? timer =false;
document.getElementById("debounce").onscroll =function(){? ?
clearTimeout(timer);// 清除未執(zhí)行的代碼,重置回初始化狀態(tài)
timer = setTimeout(function(){
console.log("函數(shù)防抖");? ?
},300);
};
函數(shù)防抖的要點(diǎn)碴萧,也是需要一個setTimeout來輔助實(shí)現(xiàn)乙嘀。延遲執(zhí)行需要跑的代碼。
如果方法多次觸發(fā)破喻,則把上次記錄的延遲執(zhí)行代碼用clearTimeout清掉虎谢,重新開始。
如果計(jì)時完畢低缩,沒有方法進(jìn)來訪問觸發(fā)嘉冒,則執(zhí)行代碼曹货。
這個方法的作用是監(jiān)聽ID為debounce元素的滾動事件
進(jìn)入滾動事件方法體的時候咆繁,做的第一件事就是清除上次未執(zhí)行的setTimeout。而setTimeout的引用id由變量timer記錄顶籽。
clearTimeout方法玩般,允許傳入無效的值。所以這里直接執(zhí)行clearTimeout即可礼饱。
然后坏为,將需要執(zhí)行的代碼放入setTimeout中,再返回setTimeout引用給timer緩存镊绪。
如果倒計(jì)時300ms以后匀伏,還沒有新的方法觸發(fā)滾動事件,則執(zhí)行setTimeout中的代碼蝴韭。
函數(shù)防抖的實(shí)現(xiàn)重點(diǎn)够颠,就是巧用setTimeout做緩存池,而且可以輕易地清除待執(zhí)行的代碼榄鉴。
其實(shí)履磨,用隊(duì)列的方式也可以做到這種效果。這里就不深入了庆尘。