當(dāng)我們監(jiān)聽一個(gè)input輸入框的onChange
事件的時(shí)候陶因,我們發(fā)現(xiàn)用戶每輸入一個(gè)數(shù)值,onChange
事件都會(huì)被不停調(diào)用,但是其實(shí)很多時(shí)候我們只關(guān)心用戶最后輸入的完整數(shù)據(jù)柄沮,不需要每次數(shù)值變化都調(diào)用change事件。還有相似的場(chǎng)景比如window的resize、scroll事件等祖搓,都是被觸發(fā)非常頻繁的事件狱意,但是我們實(shí)際又不需要那么頻繁的產(chǎn)生回調(diào)。
函數(shù)防抖(debounce)
當(dāng)持續(xù)觸發(fā)事件時(shí)拯欧,一定時(shí)間段內(nèi)沒有再觸發(fā)事件详囤,事件處理函數(shù)才會(huì)執(zhí)行一次,如果設(shè)定的時(shí)間到來之前镐作,又一次觸發(fā)了事件纬纪,就重新開始延時(shí)。如下圖滑肉,持續(xù)觸發(fā)scroll事件時(shí)包各,并不執(zhí)行handle函數(shù),當(dāng)1000毫秒內(nèi)沒有觸發(fā)scroll事件時(shí)靶庙,才會(huì)延時(shí)觸發(fā)scroll事件问畅。
也就是說防抖可以拆解成這些特性
- 觸發(fā)事件之后不會(huì)馬上執(zhí)行,而是會(huì)等n秒之后再執(zhí)行
- 如果n秒之內(nèi)不再觸發(fā)事件六荒,n秒之后就會(huì)調(diào)用回調(diào)护姆。
- 如果在n秒的過程中,你一直在觸發(fā)新的事件掏击,那么會(huì)以最近一次觸發(fā)的時(shí)間為準(zhǔn)卵皂,重新計(jì)算n秒,n秒到了才會(huì)執(zhí)行回調(diào)砚亭。
手寫防抖
功能點(diǎn):
- 可以延遲n秒之后再執(zhí)行
- 上下文當(dāng)中使用到this灯变,不能改變this指向
- 可以獲取到事件對(duì)象event
- 可以立刻執(zhí)行事件
- 返回函數(shù)執(zhí)行結(jié)果
- 可以取消防抖重新觸發(fā)
function debounce(func,wait, immediate){
var timeout,result;
var debounced = function(){
let env = this;
var args = arguments;
if(timeout){
clearTimeout(timeout);
}
if(immediate){
// 如果已經(jīng)執(zhí)行過,不再執(zhí)行
var callNow = !timeout;
timeout = setTimeout(function(){
timeout = null;
}, wait);
if (callNow) result = func.apply(context, args)
} else {
timeout = setTimeout(function(){
func.apply(env,args);
},wait);
}
return result;
}
debounced.cancel = function(){
clearTimeout(timeout);
timeout = null;
}
return debounced;
}
函數(shù)節(jié)流(throttle)
當(dāng)持續(xù)觸發(fā)事件時(shí)捅膘,保證一定時(shí)間段內(nèi)只調(diào)用一次事件處理函數(shù)添祸。節(jié)流通俗解釋就比如我們水龍頭放水,閥門一打開寻仗,水嘩嘩的往下流刃泌,秉著勤儉節(jié)約的優(yōu)良傳統(tǒng)美德,我們要把水龍頭關(guān)小點(diǎn)署尤,最好是如我們心意按照一定規(guī)律在某個(gè)時(shí)間間隔內(nèi)一滴一滴的往下滴耙替。如下圖,持續(xù)觸發(fā)scroll事件時(shí)曹体,并不立即執(zhí)行handle函數(shù)俗扇,每隔1000毫秒才會(huì)執(zhí)行一次handle函數(shù)。
- 持續(xù)觸發(fā)事件的時(shí)候混坞,一段時(shí)間只調(diào)用一次事件處理函數(shù)
- 每間隔一段時(shí)間執(zhí)行一次回調(diào)
- 首次可以執(zhí)行也可以不執(zhí)行
節(jié)流 vs 防抖
防抖側(cè)重的是延遲事件執(zhí)行
節(jié)流側(cè)重的是在某段時(shí)間內(nèi)規(guī)律執(zhí)行
手撕節(jié)流
function throttle(func, wait, options) {
var timeout, context, args, result;
var previous = 0;
if (!options) options = {};
var later = function() {
previous = options.leading === false ? 0 : new Date().getTime();
timeout = null;
func.apply(context, args);
if (!timeout) context = args = null;
};
var throttled = function() {
var now = new Date().getTime();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
};
return throttled;
}
JavaScript專題之跟著underscore學(xué)防抖
JS的防抖與節(jié)流
JavaScript專題之跟著 underscore 學(xué)節(jié)流