先奉上源碼
取自Underscore.js 1.9.1的debounce
_.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
,那麼我們首先來逐個分析它們
restArguments
// 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
_.delay
// 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
_.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;
};
轉載于:https://www.cnblogs.com/guanine/p/9597826.html