前言
在網頁實際運作的某些場景下,有些事件會不間斷的被觸發,如scroll事件,而不像我們想象中的,滾動一次觸發一次,稍微滾動一下就會觸發n多次scroll事件。如下:
window.onscroll = function (){
console.log(123);
} //監聽滾動條滑動
我隻是輕微的滾動了一下滾動條就觸發了這麼多次的scroll事件,這種情況下,由于過于頻繁地DOM操作和資源加載,嚴重影響了網頁性能,甚至會造成浏覽器崩潰。
此時,我們可以采用 debounce(防抖)和 throttle(節流)的方式來減少調用頻率,同時又不影響實際效果。
防抖
防抖通過設定setTimeout定時器的方式延遲執行,當快速多次點選的時候,每一次都會重置定時器,隻有你一段時間都不點選時定時器才能到達條件并執行事件函數。即如果觸發事件後在 n 秒内又觸發了事件,則會重新計算函數延執行時間。
如下,我們模拟一個表單送出的例子,多次快速點選送出後隻會執行一次:
<input type="submit" id="btn" value="送出">
<script>
var btn = document.getElementById('btn');
// 點選後觸發debounce()函數,第一個參數為真實要執行的函數,第二個參數為定時器延遲時間
btn.addEventListener('click',debounce(submit,1000));
//真實要執行的函數
function submit(e){
console.log("送出成功!");
console.log(this)
console.log(e);
}
//防抖函數
function debounce(fn,delay){
//設定time為定時器
var time = null;
//閉包原理,傳回一個函數
return function (e){
//如果定時器存在則清空定時器
if(time){
clearTimeout(time);
}
//設定定時器,規定時間後執行真實要執行的函數
time = setTimeout(() => {//此箭頭函數裡的this指向btn這個按鈕
fn.call(this,arguments);//改變真實要執行函數的this指向,原submit函數裡面的this指向window
},delay);
}
}
</script>
運作後,狂點送出按鈕停下來後隻會執行一次,而不會出現多次點選而多次送出。對于代碼中的閉包還有箭頭函數的this指向問題不清楚的,可以翻看我的這兩篇:閉包、箭頭函數。
節流
節流其實就很好了解了,減少一段時間的觸發頻率。簡單來說,就是你一直狂點不停的話,它會每隔一定時間就執行一次。它與防抖最大的差別就是,無論事件觸發多麼頻繁,都可以保證在規定時間内可以執行一次執行函數。下面利用計算時間戳實作:
<input type="submit" id="btn" value="送出">
<script>
var btn = document.getElementById('btn');
// 點選後觸發debounce()函數,第一個參數為真實要執行的函數,第二個參數為定時器延遲時間
btn.addEventListener('click',throttle(submit,500));
//真實要執行的函數
function submit(e){
console.log("送出成功!");
console.log(this)
// console.log(e);
}
//節流函數
function throttle(fn,delay){
//bef為上一次執行時間,初始值為0
var bef = 0;
return function (e){
//擷取目前時間戳
var now = new Date().getTime();
//如果目前時間減去上次時間大于限制時間時才執行
if(now - bef > delay){
console.log(this);
fn.call(this,arguments);
bef = now;
}
}
}
</script>
運作後,狂點不停的話,每隔500毫秒才執行一次。
也可以用定時器實作節流,如下:
<input type="submit" id="btn" value="送出">
<script>
var btn = document.getElementById('btn');
// 點選後觸發debounce()函數,第一個參數為真實要執行的函數,第二個參數為定時器延遲時間
btn.addEventListener('click',throttle(submit,500));
//真實要執行的函數
function submit(e){
console.log("送出成功!");
// console.log(this)
console.log(e);
}
//節流函數
function throttle(fn,delay){
var flag = true;
return function (e){
if(flag){
setTimeout(() => {
//到規定時間後執行函數,同時flag=true
fn(this,arguments);
flag = true;
},delay);
}
//防止一直執行
flag = false;
};
}
</script>
總結
最後我們也明白了防抖和節流的主要差別,那麼他們各自适應的場景又有哪些呢?一般當我們送出表單時使用防抖,但在頁面的無限加載場景下,我們需要使用者在滾動頁面時,每隔一段時間發一次 ajax 請求,而不是在使用者停下滾動頁面操作時才去請求資料。這樣的場景,就适合用節流技術來實作。