當用戶在網頁中進行操作時,如點選、捲動、輸入等,往往會頻繁地觸發事件。如果每個事件都立即執行相應的函數,可能會導致效能問題和使用者體驗不佳,因為這些函數可能需要執行復雜的操作,如計算、網路請求等。
為了優化這種情況,我們可以使用防抖和節流來限制函數的呼叫次數,從而提高效能和使用者體驗。
防抖
防抖是指在一定的時間間隔內,將多次觸發的事件合併成一次執行。
防抖的實現思路是:每次事件被觸發時,設定一個計時器,在指定的時間間隔內,如果該事件被再次觸發,則清除計時器並重新開始計時,直到指定的時間間隔內沒有事件觸發為止,然後呼叫函數。
防抖可以用於處理一些頻繁觸發的事件,如視窗大小改變、輸入框輸入等。以下是一個簡單的防抖函數實現:
function debounce(fn, delay) { let timer; return function() { const context = this; const args = arguments; clearTimeout(timer); timer = setTimeout(function() { fn.apply(context, args); }, delay); }; }
這個函數接收兩個引數: fn 是要防抖的函數, delay 是防抖時間間隔。返回一個新的函數,在防抖時間間隔內,重複呼叫這個新函數不會立即觸發原函數,只有等時間間隔過去之後才會觸發。
節流
節流是指在一定的時間間隔內,函數只被呼叫一次。如果在這個時間間隔內觸發了多次事件,只有第一次會呼叫函數,其餘的會被忽略。
節流的實現思路是:每次事件被觸發時,如果函數沒有在指定的時間間隔內被呼叫過,則呼叫函數並設定一個計時器,在指定的時間間隔內不再觸發事件。如果在指定的時間間隔內再次觸發了事件,則不呼叫函數,直到指定的時間間隔過去,重新開始呼叫函數。
節流可以用於處理一些頻繁觸發的事件,如頁面捲動、滑鼠移動等。以下是一個簡單的節流函數實現:
function throttle(fn, delay) { let timer; return function() { const context = this; const args = arguments; if (!timer) { timer = setTimeout(function() { fn.apply(context, args); timer = null; }, delay); } }; }
這個函數接收兩個引數: fn 是要節流的函數, delay 是節流時間間隔。返回一個新的函數,在指定的時間間隔內,只能呼叫一次原函數。
防抖和節流的實現方式可以有多種,具體實現應該根據實際場景進行調整。下面介紹一些常見的實現方式。
立即執行版和非立即執行版
防抖和節流的實現中,可以根據需求選擇立即執行版或非立即執行版。
立即執行版是指在每次觸發事件時,立即執行一次函數,然後在指定的時間間隔內不再執行。這種方式適合處理一些需要立即響應的事件,如按鈕點選等。
以下是立即執行版的防抖函數實現:
function debounce(fn, delay, immediate) { let timer; return function() { const context = this; const args = arguments; if (immediate && !timer) { fn.apply(context, args); } clearTimeout(timer); timer = setTimeout(function() { if (!immediate) { fn.apply(context, args); } timer = null; }, delay); }; }
這個函數增加了一個引數 immediate ,用來指定是否立即執行函數。如果設定為 true ,則在觸發事件時立即執行函數;否則等待指定的時間間隔後再執行。
非立即執行版是指在事件停止觸發指定時間後才執行一次函數。這種方式適合處理一些連續觸發的事件,如輸入框輸入等。
以下是非立即執行版的防抖函數實現:
function debounce(fn, delay, immediate) { let timer; return function() { const context = this; const args = arguments; if (timer) { clearTimeout(timer); } if (immediate && !timer) { fn.apply(context, args); } timer = setTimeout(function() { if (!immediate) { fn.apply(context, args); } timer = null; }, delay); }; }
這個函數與立即執行版的實現相似,區別在於多了一個 if (timer) { clearTimeout(timer); } 判斷。如果計時器已經存在,則在重新設定計時器之前先清除之前的計時器。
時間戳版和定時器版
防抖和節流的實現中,可以根據需求選擇時間戳版或定時器版。
時間戳版是指在事件觸發後,記錄下當前的時間戳,並在下次事件觸發時再次記錄時間戳。在指定的時間間隔內,如果事件再次觸發,則更新時間戳。如果時間戳與當前時間的差值超過了指定的時間間隔,則執行函數。這種方式適合處理一些需要立即響應的事件,如按鈕點選等。
以下是時間戳版的節流函數實現:
function throttle(fn, delay) { let last = 0; return function() { const context = this; const args = arguments; const now = +new Date(); if (now - last > delay) { fn.apply(context, args); last = now; } };
這個函數中, last 記錄上次執行函數的時間戳,每次事件觸發時,計算當前時間戳與上次執行函數的時間戳的差值,如果超過指定的時間間隔,則執行函數並更新 last 。
定時器版是指在事件觸發後,設定一個定時器,在指定的時間間隔內不再觸發事件。如果在指定的時間間隔內再次觸發了事件,則不執行函數,直到定時器到期後再次執行函數。這種方式適合處理一些連續觸發的事件,如頁面捲動、滑鼠移動等。
以下是定時器版的節流函數實現:
function throttle(fn, delay) { let timer; return function() { const context = this; const args = arguments; if (!timer) { timer = setTimeout(function() { fn.apply(context, args); timer = null; }, delay); } }; }
這個函數與之前介紹的非立即執行版的防抖函數實現類似,區別在於將 setTimeout 的時間間隔設定為指定的時間間隔,而不是事件觸發後的延遲時間。
防抖和節流的主要區別在於它們限制函數呼叫的方式不同。防抖是指在指定的時間間隔內將多次觸發的事件合併成一次執行,而節流是指在指定的時間間隔內,函數只被呼叫一次。同時,防抖和節流也有一些缺點,比如可能會導致事件響應延遲或事件被忽略等問題。因此,在實際使用中,需要根據場景進行權衡和選擇。