函式節流和去抖都是限制基於DOM事件執行的javascript數量的方法,都是為了提高JS效能,但是兩者是有區別的。
函式節流
調節強制執行一段時間內可以呼叫函式的最大次數,如“每100毫秒最多執行一次”。 在正常情況下,我們可以在10秒鐘內呼叫此函式1000次。如果您每100毫秒將其限制為僅一次,則最多隻能執行該功能100次。
函式去抖
強制一個函式在一段時間內只被呼叫一次。如“一個函式100毫秒內只執行一次”。 也許一個函式在很集中的時間內被呼叫1000次,超過3秒,然後停止呼叫。如果我們在100毫秒內將其去抖動,該函式將僅啟動一次3.1秒。函式去抖就是對於一定時間段的連續的函式呼叫,只讓其執行一次。
重點
這些概念的一個主要用例是某些DOM事件,如滾動和調整大小。例如,如果我們將一個滾動處理程式附加到元素,並將該元素向下滾動到5000px,則可能會看到超過100個事件觸發。如果我們的事件處理程式執行一些工作(如重計算和其他DOM操作),我們可能會看到效能問題。如果你可以使執行該處理程式的次數減少,而沒有太多的中斷,那這些工作是值得的。
- 一些小例子
- 等到使用者停止調整視窗大小
- 在使用者停止打字之前,不要觸發ajax事件
- 計算頁面的滾動位置,每50ms最多執行一次
- 在應用程式中拖動元素時,請確保良好的效能
如何實現
這兩個功能都在 Underscore 和 Lodash 中已經實現。當然即使我們不使用這些庫,我們也可以隨時從中抽出這些功能供自己使用。
-
throttled scroll:
/** * 頻率控制 返回函式連續呼叫時,func 執行頻率限定為 次 / wait * * @param {function} func 傳入函式 * @param {number} wait 表示時間視窗的間隔 * @param {object} options 如果想忽略開始邊界上的呼叫,傳入{leading: false}。 * 如果想忽略結尾邊界上的呼叫,傳入{trailing: false} * @return {function} 返回客戶呼叫函式 */ _.throttle = function(func, wait, options) { var context, args, result; var timeout = null; // 上次執行時間點 var previous = 0; if (!options) options = {}; // 延遲執行函式 var later = function() { // 若設定了開始邊界不執行選項,上次執行時間始終為0 previous = options.leading === false ? 0 : _.now(); timeout = null; result = func.apply(context, args); if (!timeout) context = args = null; }; return function() { var now = _.now(); // 首次執行時,如果設定了開始邊界不執行選項,將上次執行時間設定為當前時間。 if (!previous && options.leading === false) previous = now; // 延遲執行時間間隔 var remaining = wait - (now - previous); context = this; args = arguments; // 延遲時間間隔remaining小於等於0,表示上次執行至此所間隔時間已經超過一個時間視窗 // remaining大於時間視窗wait,表示客戶端系統時間被調整過 if (remaining <= 0 || remaining > wait) { clearTimeout(timeout); timeout = null; previous = now; result = func.apply(context, args); if (!timeout) context = args = null; //如果延遲執行不存在,且沒有設定結尾邊界不執行選項 } else if (!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining); } return result; }; }; 複製程式碼
-
debounced resize:
/** * 空閒控制 返回函式連續呼叫時,空閒時間必須大於或等於 wait,func 才會執行 * * @param {function} func 傳入函式 * @param {number} wait 表示時間視窗的間隔 * @param {boolean} immediate 設定為ture時,呼叫觸發於開始邊界而不是結束邊界 * @return {function} 返回客戶呼叫函式 */ _.debounce = function(func, wait, immediate) { var timeout, args, context, timestamp, result; var later = function() { // 據上一次觸發時間間隔 var last = _.now() - timestamp; // 上次被包裝函式被呼叫時間間隔last小於設定時間間隔wait if (last < wait && last > 0) { timeout = setTimeout(later, wait - last); } else { timeout = null; // 如果設定為immediate===true,因為開始邊界已經呼叫過了此處無需呼叫 if (!immediate) { result = func.apply(context, args); if (!timeout) context = args = null; } } }; return function() { context = this; args = arguments; timestamp = _.now(); var callNow = immediate && !timeout; // 如果延時不存在,重新設定延時 if (!timeout) timeout = setTimeout(later, wait); if (callNow) { result = func.apply(context, args); context = args = null; } return result; }; }; 複製程式碼
應用場景
-
函式節流
- DOM 元素的拖拽功能實現(mousemove)
- 計算滑鼠移動的距離(mousemove)
- 搜尋聯想(keyup)
- 監聽滾動事件判斷是否到頁面底部自動載入更多內容
-
函式去抖
- 視窗縮放,每次resize/scroll觸發事件
- 文字輸入的驗證(連續輸入文字後傳送 AJAX 請求進行驗證,驗證一次就好)