防抖或是節(jié)流,都是為了限制函數(shù)執(zhí)行的次數(shù)。
防抖:通過setTimeout,在一定時間間隔內,將多次觸發(fā)變?yōu)橐淮斡|發(fā)。
節(jié)流:減少一段時間內的觸發(fā)次數(shù)。(控制流量)
防抖:
// 模擬表單提交
const btn = document.getElementById('btn');
btn.addEventListener('click', debounce(submit, 1000));
一、最基礎版本,實現(xiàn)1秒內只提交一次:
思路:
- addEventListener的第二個參數(shù)為一個函數(shù),而不是直接函數(shù)調用,所以debounce要返回一個函數(shù)
- 最外面聲明timer,有timer則清除,重新計時1s后submit。這樣總是最后一次觸發(fā)1s后才會submit。
function debounce(fn, delay){
var timer = null;
return function(){
if(timer){ clearTimeout(timer); }
timer = setTimeout(() => {
fn();
}, delay);
}
}
function submit(){
console.log('submit');
}
這樣存其實依然存在問題,如何在提交函數(shù)(submit)中拿到正確的事件參數(shù)e,以及this(this應該是指向btn)?
e是通過addEventListener第二個參數(shù)回調函數(shù)傳遞的,也就是在debounce函數(shù)return的匿名函數(shù)中,可以拿到e,直接傳給fn(timer的回調是箭頭函數(shù),所以可以直接拿到外部綁定的this和arguments)。
為了兼容除e之外的未知參數(shù),使用arguments形式傳遞參數(shù)。
apply直接傳遞數(shù)組形式arguments并改變fn中的this指向為箭頭函數(shù)綁定的this,也就是指向btn。
function debounce(fn, delay){
var timer = null;
return function(){
console.log(e); // PointerEvent
if(timer){ clearTimeout(timer); }
timer = setTimeout(() => {
console.log(this, arguments); // btn Arguments: []
fn.apply(this, arguments);
}, delay);
}
}
function submit(){
console.log('this:', this); // btn
console.log('e:', arguments[0]); // 輸出PointerEvent
}
二、基本的防抖已實現(xiàn),但還有一個小問題:每次的submit執(zhí)行總會延遲1s,這樣適用于類似監(jiān)聽搜索框變化,發(fā)起搜索的場景,而對于類似表單提交場景是不需要延遲的,這樣用戶體驗是不友好的,如何能在第一次點擊時可以立即執(zhí)行?(換句話說,只在連續(xù)點擊的第一次點擊時立即執(zhí)行,后面的點擊無響應)
function debounce(fn, delay){
var timer = null;
return function(){
const firstClick = !timer; // firstClick標記是否第一次點擊,通過timer來判斷
if(timer){ clearTimeout(timer); }
if(firstClick){
fn.apply(this, arguments); // 如果是第一次,立即執(zhí)行
}
timer = setTimeout(() => {
timer = null; // 1s后timer置為初始值null(不能clearTimeout,這樣timer依然存在,值為number編號)
}, delay);
}
}
節(jié)流
最外層初始化begin為0,第一次點擊的時間戳和begin比較必然大于delay,后面每次點擊,判斷距離上次執(zhí)行submit事件大于2s才再次執(zhí)行,并更新begin
const btn = document.getElementById('btn');
btn.addEventListener('click', throttle(submit, 2000));
function throttle(fn, delay){
var begin = 0;
return function(){
var cur = new Date().getTime();
if(cur - begin > delay){
console.log(cur - begin);
submit();
begin = cur;
}
}
}
function submit(){
console.log('submit');
}
// 連續(xù)多次點擊,log輸出:
1635479979895
submit
13143
submit
2073
submit
2002
submit
2019
submit