前言
在網頁實際運行的某些場景下,有些事件會不間斷的被觸發,如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 請求,而不是在用戶停下滾動頁面操作時才去請求數據。這樣的場景,就適合用節流技術來實現。
參考文章:https://bbs.huaweicloud.com/blogs/297720?utm_source=cnblog&utm_medium=bbs-ex&utm_campaign=other&utm_content=content

