我們知道,在一個(gè)高頻觸發(fā)的監(jiān)聽事件中,會(huì)不斷觸發(fā)所綁定的方法,如onscroll、onmouseover、onkeyup等,在你連續(xù)觸發(fā)該事件,就會(huì)不斷執(zhí)行綁定方法。也許你綁定的事件不復(fù)雜,加上瀏覽器性能處理越來越好,你會(huì)覺得這沒什么,但是,如果綁定的方法執(zhí)行的內(nèi)容比較復(fù)雜,涉及DOM重排重繪嚴(yán)重或者請(qǐng)求頻繁,那么如此高頻觸發(fā),必將帶來性能和交互的嚴(yán)重不友好。
為了解決出現(xiàn)這種情況,防抖和節(jié)流,這兩種方案為此誕生。
它們都是有一個(gè)時(shí)間規(guī)定,在這個(gè)規(guī)定下限制某個(gè)方法的執(zhí)行時(shí)機(jī)。
它們有個(gè)共同點(diǎn),就是指定時(shí)間內(nèi),只能執(zhí)行一次。https://www.lodashjs.com/docs/lodash.debounce
防抖:
指定時(shí)間內(nèi),方法只能執(zhí)行一次。而這個(gè)時(shí)間的計(jì)算,是從最后一次觸發(fā)監(jiān)聽事件開始算起。一般表現(xiàn)為,在一段連續(xù)觸發(fā)的事件中,最終會(huì)轉(zhuǎn)化為一次方法執(zhí)行,就像防止抖動(dòng)一樣,你做一個(gè)事,防止你手抖不小心重復(fù)干了
原理:在每次函數(shù)執(zhí)行之前先清空上一次設(shè)置的定時(shí)器,原因是:如果執(zhí)行間隔大于n秒,那么先前的定時(shí)任務(wù)一定已經(jīng)執(zhí)行完畢,當(dāng)執(zhí)行clearTimeout時(shí)其實(shí)并沒有定時(shí)器可清除;否則定時(shí)器就會(huì)被清除,然后重新計(jì)時(shí)
思路:
每次觸發(fā)事件時(shí)都取消之前的延時(shí)調(diào)用方法
function debounce(fun, delay) {
var timeout;
return function() {
let that = this;
console.log('arguments', arguments);
clearTimeout(timeout);
let args = arguments;
timeout = setTimeout(function() {
console.log('----', that);
fun.call(that, ...args);
//是函數(shù)的this指定input, fn()的this是window
//在setTimeout里面執(zhí)行的函數(shù)都會(huì)在全局作用域執(zhí)行所以this會(huì)指向window,
//但是fn執(zhí)行時(shí)this指向的上下文環(huán)境應(yīng)該是當(dāng)前執(zhí)行的函數(shù),所以要用apply來綁定fn的this指定到當(dāng)前
}, delay);
};
}
let inputb = document.getElementById('debounce');
let debounceAjax = debounce(ajax, 2000);
inputb.addEventListener('keyup', function(e) {
debounceAjax.call(this, e.target.value);//是函數(shù)的this指定input
// debounceAjax(e.target.value);
});
節(jié)流:
指定時(shí)間內(nèi),方法只能執(zhí)行一次。而這個(gè)時(shí)間的計(jì)算,是從上次執(zhí)行方法開始算起。一般表現(xiàn)為,在一段連續(xù)觸發(fā)的事件中,根據(jù)你設(shè)定的時(shí)間間隔,降低觸發(fā)頻率,重復(fù)執(zhí)行
思路:
每次觸發(fā)事件時(shí)都判斷當(dāng)前是否有等待執(zhí)行的延時(shí)函數(shù)
原理:在每次函數(shù)執(zhí)行之前先判斷是否存在定時(shí)器,存在則跳過本次執(zhí)行,否則設(shè)置新的定時(shí)器
function throttle(fun, delay) {
let last, deferTimer;
return function(args) {
let that = this;
let now = +new Date();
if (last && now < last + delay) {
clearTimeout(deferTimer);
deferTimer = setTimeout(function() {
last = now;
fun.apply(that, arguments);
}, delay);
} else {
last = now;
fun.apply(that, arguments);
}
};
}
let throttleAjax = throttle(ajax, 1000);
let inputc = document.getElementById('throttle');
inputc.addEventListener('keyup', function(e) {
throttleAjax(e.target.value);
});