函數(shù)函數(shù)我們先來看一看防抖和節(jié)流比較精煉的定義:
防抖: 指觸發(fā)事件后在 n 秒內(nèi)函數(shù)只能執(zhí)行一次,如果在 n 秒內(nèi)又觸發(fā)了事件,則會重新計(jì)算函數(shù)執(zhí)行時(shí)間。
節(jié)流:連續(xù)發(fā)生的事件在n秒內(nèi)只執(zhí)行一次
相信,看完上面說的定義后,有些人還是不能很好的明白他們之間的區(qū)別,那么就接下來談一談我對它們的理解吧。
防抖(debounce)
防抖防抖,顧名思義,就是防止手抖么,你想一下你手抖會干什么(???停止開車)比如一個(gè)按鈕,點(diǎn)一下向服務(wù)器發(fā)送一個(gè)請求,你手一抖,點(diǎn)了好幾下,一下就向服務(wù)器發(fā)了好幾次請求,這不浪費(fèi)時(shí)間資源么。這時(shí)候,就要有個(gè)防抖函數(shù)來幫幫你了。
當(dāng)你點(diǎn)擊這個(gè)按鈕后,不管你手怎么抖,一直抖個(gè)不停的點(diǎn)這個(gè)按鈕,都只向服務(wù)器發(fā)送一次請求,只有過了一段時(shí)間后,確定了你手不再抖后(在這一段時(shí)間你都沒有再點(diǎn)這個(gè)按鈕),你再去點(diǎn)擊這個(gè)按鈕,才會再次向服務(wù)器發(fā)送請求。
下面分析一下代碼,你一定會更加的明白:
/*
@function 防抖函數(shù)(最后執(zhí)行)
@param func {Function} 要執(zhí)行的函數(shù)
@para wait {Number} 判斷手不抖的時(shí)間
*/
const debounce=(func,wait)=>{
let timeout; //不能定義到里面,否則每次調(diào)用里面的函數(shù),都會對timeout重新賦值
return function(){
if(timeout) clearTimeout(timeout) //這里使用timeout產(chǎn)生了閉包
timeout = setTimeout(function(){
func.apply(this)
},wait)
}
}
function btnClick(){
console.log('發(fā)送一次請求')
}
$('#myBtn').click(debounce(btnClick,3000));
首先會執(zhí)行第18行的debounce函數(shù),返回一個(gè)匿名函數(shù)作為按鈕點(diǎn)點(diǎn)擊的回調(diào)。當(dāng)點(diǎn)擊一次按鈕的時(shí)候,timeout為undefined,會執(zhí)行第10行的代碼,設(shè)置一個(gè)定時(shí)器。當(dāng)你手抖(在wait時(shí)間內(nèi)有點(diǎn)擊了按鈕)的時(shí)候,會執(zhí)行第9行,將上一個(gè)定時(shí)器清除,然后再設(shè)置一個(gè)新的定時(shí)器。只有你手不抖了(在wait時(shí)間內(nèi)沒有點(diǎn)擊按鈕),就會執(zhí)行第11行,執(zhí)行點(diǎn)擊按鈕所要發(fā)送的請求服務(wù)。
可以看出來,這個(gè)防抖函數(shù),是再確定你手不抖前的最后一次點(diǎn)擊才發(fā)送的請求數(shù)據(jù)。但有時(shí)候你并不想這樣啊,你覺得,當(dāng)我第一次點(diǎn)擊的時(shí)候就要發(fā)送數(shù)據(jù),至于后面我手抖,你控制不讓我發(fā)不久好了,這樣我手抖的時(shí)候,發(fā)現(xiàn)數(shù)據(jù)已經(jīng)請求過來了,我一高興,手不就不抖了,好的,滿足你的要求。
/*
@function 防抖函數(shù)(最初執(zhí)行)
@param func {Function} 要執(zhí)行的函數(shù)
@para wait {Number} 判斷手不抖的時(shí)間
*/
const debounce=(func,wait)=>{
let timeout;
return function(){
if(timeout){
clearTimeout(timeout);
}else{
func.apply(this)
}
timeout = setTimeout(function(){
timeout =null
},wait)
}
}
function btnClick(){
console.log('發(fā)送一次請求')
}
$('#myBtn').click(debounce(btnClick,3000));
相當(dāng)于,在第一次點(diǎn)擊,還沒有設(shè)置定時(shí)器的時(shí)候,就先執(zhí)行請求數(shù)據(jù)的函數(shù),然后設(shè)置定時(shí)器用來防抖,后面不管怎么抖,timeout值都存在,只有再不抖后,才會將timeout設(shè)置為null,然后再點(diǎn)擊的時(shí)候就又能請求數(shù)據(jù)了。
節(jié)流(throttle)
節(jié)流節(jié)流,顧名思義就是節(jié)省流量了,你想想,怎么才能節(jié)省流量。那不就減少請求唄。1秒請求3次,我改成3秒請求1次,那流量還不妥妥的節(jié)省下來了。就比如,你一個(gè)搜索框,按一下鍵盤向服務(wù)器發(fā)送一次請求,卡卡的,頁面一直跟著你敲鍵盤在變化,你給老板說,看帥不,實(shí)時(shí)動態(tài)搜索,這時(shí)候老板上來就給你兩耳刮子,說著,我可算知道為啥一個(gè)小項(xiàng)目就要買這么好的服務(wù)器了,都是你這玩意霍霍的。這時(shí)候,節(jié)流就能幫助到你了,設(shè)置個(gè)時(shí)間,這個(gè)時(shí)間內(nèi),不管怎么敲鍵盤,只請求一次數(shù)據(jù),差不多輸一個(gè)單詞請求一次,大大節(jié)省了后臺的壓力,你的動態(tài)搜索也能夠很好的實(shí)現(xiàn)。
估計(jì)這個(gè)還是比較好理解的,直接看代碼就好了。
/*
@function 節(jié)流函數(shù)
@param func {Function} 要執(zhí)行的函數(shù)
@para wait {Number} 時(shí)間間隔
*/
const throttle=(func,wait)=>{
let previous = 0;
return function(){
let nowtime = Date.now();
if(nowtime-previous>wait){
func.apply(this);
previous = nowtime;
}
}
}
function keyUp(){
console.log('搜索一次')
}
$('#myInput').keyup(throttle(btnClick,1000));
這個(gè)代碼也比較好理解,每次按鍵抬起的時(shí)候,都會判斷當(dāng)前的時(shí)間和上一次執(zhí)行搜索功能的時(shí)間的差值,判斷要不要再進(jìn)行一次搜索。
好了,函數(shù)的防抖與節(jié)流的區(qū)別和實(shí)現(xiàn)應(yīng)該很清楚了。
最后不得不感慨,中華文化的博大精深以及前輩們翻譯時(shí)的智慧,debounce與throttle對應(yīng)防抖與節(jié)流,讓這兩個(gè)很容易搞混的概念一下子就變得如此清晰。