取自Underscore.js 1.9.1的debouncees6
_.debounce = function(func, wait, immediate) { var timeout, result; var later = function(context, args) { timeout = null; if (args) result = func.apply(context, args); }; var debounced = restArguments(function(args) { if (timeout) clearTimeout(timeout); if (immediate) { var callNow = !timeout; timeout = setTimeout(later, wait); if (callNow) result = func.apply(this, args); } else { timeout = _.delay(later, wait, this, args); } return result; }); debounced.cancel = function() { clearTimeout(timeout); timeout = null; }; return debounced; };
其中比較陌生的是restArguments
和_.delay
,那麼咱們首先來逐個分析它們app
// Some functions take a variable number of arguments, or a few expected // arguments at the beginning and then a variable number of values to operate // on. This helper accumulates all remaining arguments past the function’s // argument length (or an explicit `startIndex`), into an array that becomes // the last argument. Similar to ES6’s "rest parameter". var restArguments = function(func, startIndex) { startIndex = startIndex == null ? func.length - 1 : +startIndex; return function() { var length = Math.max(arguments.length - startIndex, 0), rest = Array(length), index = 0; for (; index < length; index++) { rest[index] = arguments[index + startIndex]; } // 我的以爲這段switch沒有特別意義,能夠刪除 // switch (startIndex) { // case 0: 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); // } var args = Array(startIndex + 1); for (index = 0; index < startIndex; index++) { args[index] = arguments[index]; } args[startIndex] = rest; return func.apply(this, args); }; };
它很相似ES6
的剩餘參數
舉個例子函數
function sum (a, b, rest) { var sum = a + b; console.log(Array.isArray(rest)); // 打印true if (rest.length) { sum += rest.reduce((x, y) => x + y); } return sum; } ra_sum = restArguments(sum); console.log(ra_sum(1, 2)); // 8 console.log(ra_sum(1, 2, 3, 4, 5)); // 15 // 利用ES6的剩餘參數能夠這樣寫 function es6_ra_sum(a, b, ...rest) { var sum = a + b; console.log(rest) console.log(Array.isArray(rest)); // 打印true if (rest.length) { sum += rest.reduce((x, y) => x + y); } return sum; } console.log(es6_ra_sum(1, 2)); // 3 console.log(es6_ra_sum(1, 2, 3, 4, 5)); // 15
// Delays a function for the given number of milliseconds, and then calls // it with the arguments supplied. _.delay = restArguments(function(func, wait, args) { return setTimeout(function() { return func.apply(null, args); }, wait); }); // 至關於 _.delay = function(func, wait, ...args) { return setTimeout(function() { return func.apply(null, args); }, wait); }
_.debounce = function(func, wait, immediate) { var timeout, result; var later = function(context, args) { timeout = null; // 重置timeout爲了leading執行 // 判斷arg是爲了下面運行timeout = setTimeout(later, wait);這句話時func不會被執行 if (args) result = func.apply(context, args); }; // 本來來是restArgumenst返回函數,這裏爲了直觀我直接換成es6的剩餘參數形式 var debounced = function(...args) { if (timeout) clearTimeout(timeout); if (immediate) { //初始的時候timeout爲undefined,later函數運行的時候置爲null, 這兩種狀況callNow爲true var callNow = !timeout; // 下面這句話的目的不是爲了執行func而是切換timeout的值,也就是間接改變callNow。並且later中args並無傳入因此不會執行later中不會執行func timeout = setTimeout(later, wait); // 這句話纔是當immediate爲true時真正地執行func if (callNow) result = func.apply(this, args); } else { // trailing執行func timeout = _.delay(later, wait, this, args); // 至關於setTimeout(function() { // return later.apply(null, [this, args]); // }, wait); // 再在later中運行result = func.apply(this, args); 最後和callNow的時候運行一致 } return result; } debounced.cancel = function() { clearTimeout(timeout); timeout = null; }; return debounced; };