debounce(防抖):當調用動做n毫秒後,纔會執行該動做,若在這n毫秒內又調用此動做則將從新計算執行時間。好比:若是用手指一直按住一個彈簧,它將不會彈起直到你鬆手爲止。css
throttle(節流):預先設定一個執行週期,當調用動做的時刻大於等於執行週期則執行該動做,而後進入下一個新週期。好比:將水龍頭擰緊直到水是以水滴的形式流出,那你會發現每隔一段時間,就會有一滴水流出。數組
window對象的resize、scroll事件app
拖拽時的mousemove事件函數
射擊遊戲中的mousedown、keydown事件佈局
文字輸入、自動完成的keyup事件ui
實際上對於window的resize事件,實際需求大多爲中止改變大小n毫秒後執行後續處理 (防抖);而其餘事件大多的需求是以必定的頻率執行後續處理(節流)。this
增長一個輔助函數 restArgs.net
/** * 類ES6 rest參數的實現,使某個函數具有支持rest參數的能力 * @param func 須要rest參數的函數 * @param startIndex 從哪裏開始標識rest參數, 若是不傳遞, 默認最後一個參數爲rest參數 * @returns {Function} 返回一個具備rest參數的函數 */ var restArgs = function (func, startIndex) { // rest參數從哪裏開始,若是沒有,則默認視函數最後一個參數爲rest參數 // 注意, 函數對象的length屬性, 揭示了函數的參數個數 /* ex: function add(a,b) {return a+b;} console.log(add.length;) // 2 */r startIndex = startIndex == null ? func.length - 1 : +startIndex; // 返回一個支持rest參數的函數 return function () { // 校訂參數, 以避免出現負值狀況 var length = Math.max(arguments.length - startIndex, 0); // 爲rest參數開闢數組存放 var rest = Array(length); // 假設參數從2個開始: func(a,b,*rest) // 調用: func(1,2,3,4,5); 實際的調用是:func.call(this, 1,2, [3,4,5]); for (var index = 0; index < length; index++) { rest[index] = arguments[index + startIndex]; } // 根據rest參數不一樣, 分狀況調用函數, 須要注意的是, rest參數老是最後一個參數, 不然會有歧義 switch (startIndex) { case 0: // call的參數一個個傳 return func.call(this, rest); case 1: return func.call(this, arguments[0], rest); case 2: return func.call(this, arguments[0], arguments[1], rest); } // 若是不是上面三種狀況, 而是更通用的(應該是做者寫着寫着發現這個switch case可能越寫越長, 就用了apply) var args = Array(startIndex + 1); // 先拿到前面參數 for (index = 0; index < startIndex; index++) { args[index] = arguments[index]; } // 拼接上剩餘參數 args[startIndex] = rest; return func.apply(this, args); }; };
返回 function 函數的防反跳版本, 將延遲函數的執行(真正的執行)在函數最後一次調用時刻的 wait 毫秒以後. 對於必須在一些輸入(可能是一些用戶操做)中止到達以後執行的行爲有幫助。 例如: 渲染一個Markdown格式的評論預覽, 當窗口中止改變大小以後從新計算佈局, 等等.rest
傳參 immediate 爲 true, debounce會在 wait 時間間隔的開始調用這個函數 。在相似不當心點了提交按鈕兩下而提交了兩次的狀況下頗有用。code
var debounce = function (func, wait, immediate) { var timeout, result; var later = function (context, args) { timeout = null; if (args) result = func.apply(context, args); }; var debounced = restArgs(function (args) { // 一旦存在timeout, 意味以前嘗試調用過func // 因爲debounce只認最新的一次調用, 因此以前等待執行的func都會被終止 if (timeout) clearTimeout(timeout); // 若是容許新的調用嘗試當即執行, if (immediate) { // 若是以前尚沒有調用嘗試,那麼這次調用能夠立馬執行,不然必定得等待以前的執行完畢 var callNow = !timeout; // 刷新timeout timeout = setTimeout(later, wait); // 若是能被當即執行,當即執行 if (callNow) result = func.apply(this, args); } else { // 不然,此次嘗試調用會延時wait個時間 timeout = delay(later, wait, this, args); } return result; }); debounced.cancel = function () { clearTimeout(timeout); timeout = null; }; return debounced; };
建立並返回一個像節流閥同樣的函數,當重複調用函數的時候,至少每隔 wait毫秒調用一次該函數。對於想控制一些觸發頻率較高的事件有幫助。
默認狀況下,throttle將在你調用的第一時間儘快執行這個function,而且,若是你在wait週期內調用任意次數的函數,都將盡快的被覆蓋。若是你想禁用第一次首先執行的話,傳遞{leading: false},還有若是你想禁用最後一次執行的話,傳遞{trailing: false}。
var throttle = function (func, wait, options) { var timeout, context, args, result; // 最近一次func被調用的時間點 var previous = 0; if (!options) options = {}; // 建立一個延後執行的函數包裹住func的執行過程 var later = function () { // 執行時,刷新最近一次調用時間 previous = options.leading === false ? 0 : new Date(); // 清空定時器 timeout = null; result = func.apply(context, args); if (!timeout) context = args = null; }; // 返回一個throttled的函數 var throttled = function () { // ----- 節流函數開始執行---- // 咱們嘗試調用func時,會首先記錄當前時間戳 var now = new Date(); // 是不是第一次調用 if (!previous && options.leading === false) previous = now; // func還要等待多久才能被調用 = 預設的最小等待期-(當前時間-上一次調用的時間) // 顯然,若是第一次調用,且未設置options.leading = false,那麼remaing=0,func會被當即執行 var remaining = wait - (now - previous); // 記錄以後執行時須要的上下文和參數 context = this; args = arguments; // 若是計算後能被當即執行 if (remaining <= 0 || remaining > wait) { // 清除以前的「最新調用」 if (timeout) { clearTimeout(timeout); timeout = null; } // 刷新最近一次func調用的時間點 previous = now; // 執行func調用 result = func.apply(context, args); // 若是timeout被清空了, if (!timeout) context = args = null; } else if (!timeout && options.trailing !== false) { // 若是設置了trailing edge,那麼暫緩這次調用嘗試的執行 timeout = setTimeout(later, remaining); } return result; }; // 能夠取消函數的節流化 throttled.cancel = function () { clearTimeout(timeout); previous = 0; timeout = context = args = null; }; return throttled; };
參考文章
http://www.tuicool.com/articl...
http://blog.csdn.net/jinboker...
http://www.css88.com/doc/unde...