前言
在前端開發的過程中,我們經常會需要繫結一些持續觸發的事件,如 resize、scroll、mousemove 等等,但有些時候我們並不希望在事件持續觸發的過程中那麼頻繁地去執行函式。這時候就用到防抖與節流。
函式防抖(debounce):
當持續觸發事件時,一定時間段內沒有再觸發事件,事件處理函式才會執行一次,如果設定的時間到來之前,又一次觸發了事件,就重新開始延時。如下圖,持續觸發scroll事件時,並不執行handle函式,當1000毫秒內沒有觸發scroll事件時,才會延時觸發scroll事件。
非立即執行版
function debounce(func, wait) {
let timeout;
return function () {
let context = this;
let args = arguments;
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(context, args)
}, wait);
}
}
複製程式碼
非立即執行版的意思是觸發事件後函式不會立即執行,而是在 n 秒後執行,如果在 n 秒內又觸發了事件,則會重新計算函式執行時間。
立即執行版
function debounce(func,wait) {
let timeout;
return function () {
let context = this;
let args = arguments;
if (timeout) clearTimeout(timeout);
let callNow = !timeout;
timeout = setTimeout(() => {
timeout = null;
}, wait)
if (callNow) func.apply(context, args)
}
}
複製程式碼
立即執行版的意思是觸發事件後函式會立即執行,然後 n 秒內不觸發事件才能繼續執行函式的效果
防抖應用場景
- 搜尋框輸入查詢,如果使用者一直在輸入中,沒有必要不停地呼叫去請求服務端介面,等使用者停止輸入的時候,再呼叫,設定一個合適的時間間隔,有效減輕服務端壓力。
- 表單驗證
- 按鈕提交事件。
- 瀏覽器視窗縮放,resize事件(如視窗停止改變大小之後重新計算佈局)等。
函式節流(throttle):
當持續觸發事件時,保證一定時間段內只呼叫一次事件處理函式。節流通俗解釋就比如我們水龍頭放水,閥門一開啟,水嘩嘩的往下流,秉著勤儉節約的優良傳統美德,我們要把水龍頭關小點,最好是如我們心意按照一定規律在某個時間間隔內一滴一滴的往下滴。如下圖,持續觸發scroll事件時,並不立即執行handle函式,每隔1000毫秒才會執行一次handle函式。函式節流主要有兩種實現方法:時間戳和定時器
時間戳版:
function throttle(func, wait) {
let previous = 0;
return function() {
let now = Date.now();
let context = this;
let args = arguments;
if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
}
}
複製程式碼
定時器版:
function throttle(func, wait) {
let timeout;
return function() {
let context = this;
let args = arguments;
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args)
}, wait)
}
}
}
複製程式碼
節流應用場景
- 按鈕點選事件
- 拖拽事件
- onScoll
- 計算滑鼠移動的距離(mousemove)
總結
-
函式防抖:將幾次操作合併為一此操作進行。原理是維護一個計時器,規定在delay時間後觸發函式,但是在delay時間內再次觸發的話,就會取消之前的計時器而重新設定。這樣一來,只有最後一次操作能被觸發。
-
函式節流:使得一定時間內只觸發一次函式。原理是通過判斷是否到達一定時間來觸發函式。
區別:
函式節流不管事件觸發有多頻繁,都會保證在規定時間內一定會執行一次真正的事件處理函式,而函式防抖只是在最後一次事件後才觸發一次函式。 比如在頁面的無限載入場景下,我們需要使用者在滾動頁面時,每隔一段時間發一次 Ajax 請求,而不是在使用者停下滾動頁面操作時才去請求資料。這樣的場景,就適合用節流技術來實現。