Debounce 和 Throttle 的原理及實現瀏覽器
在處理諸如 resize、scroll、mousemove 和 keydown/keyup/keypress 等事件的時候,一般咱們不但願這些事件太過頻繁地觸發,尤爲是監聽程序中涉及到大量的計算或者有很是耗費資源的操做。閉包
有多頻繁呢?以 mousemove 爲例,根據 DOM Level 3 的規定,「若是鼠標連續移動,那麼瀏覽器就應該觸發多個連續的 mousemove 事件」,這意味着瀏覽器會在其內部計時器容許的狀況下,根據用戶移動鼠標的速度來觸發 mousemove 事件。(固然了,若是移動鼠標的速度足夠快,好比「刷」一下掃過去,瀏覽器是不會觸發這個事件的)。resize、scroll 和 key* 等事件與此相似。app
function debounce(fn, delay) { // 定時器,用來 setTimeout var timer // 返回一個函數,這個函數會在一個時間區間結束後的 delay 毫秒時執行 fn 函數 return function () { // 保存函數調用時的上下文和參數,傳遞給 fn var context = this var args = arguments // 每次這個返回的函數被調用,就清除定時器,以保證不執行 fn clearTimeout(timer) // 當返回的函數被最後一次調用後(也就是用戶中止了某個連續的操做), // 再過 delay 毫秒就執行 fn timer = setTimeout(function () { fn.apply(context, args) }, delay) } }
debounce 返回了一個閉包,這個閉包依然會被連續頻繁地調用,可是在閉包內部,卻限制了原始函數 fn 的執行,強制 fn 只在連續操做中止後只執行一次。函數
throttle 的概念理解起來更容易,就是固定函數執行的速率,即所謂的「節流」。正常性能
/** * * @param fn {Function} 實際要執行的函數 * @param delay {Number} 執行間隔,單位是毫秒(ms) * * @return {Function} 返回一個「節流」函數 */ function throttle(fn, threshhold) { // 記錄上次執行的時間 var last // 定時器 var timer // 默認間隔爲 250ms threshhold || (threshhold = 250) // 返回的函數,每過 threshhold 毫秒就執行一次 fn 函數 return function () { // 保存函數調用時的上下文和參數,傳遞給 fn var context = this var args = arguments var now = +new Date() // 若是距離上次執行 fn 函數的時間小於 threshhold,那麼就放棄 // 執行 fn,並從新計時 if (last && now < last + threshhold) { clearTimeout(timer) // 保證在當前時間區間結束後,再執行一次 fn timer = setTimeout(function () { last = now fn.apply(context, args) }, threshhold) // 在時間區間的最開始和到達指定間隔的時候執行一次 fn } else { last = now fn.apply(context, args) } } }
二者應用以後,直接帶來的效率。若是仍是不能徹底體會 debounce 和 throttle 的差別,能夠到 這個頁面 看一下二者可視化的比較。
參考地址this