/**
* 防抖函數(shù)晨缴,返回函數(shù)連續(xù)調(diào)用時,空閑時間必須大于或等于 wait荆烈,func 才會執(zhí)行
*
* @param {function} func 回調(diào)函數(shù)
* @param {number} wait 表示時間窗口的間隔
* @param {boolean} immediate 設(shè)置為ture時挡育,是否立即調(diào)用函數(shù)
* @return {function} 返回客戶調(diào)用函數(shù)
*/
function debounce (func, wait = 50, immediate = true) {
let timer, context, args
// 延遲執(zhí)行函數(shù)
const later = () => setTimeout(() => {
// 延遲函數(shù)執(zhí)行完畢腾誉,清空緩存的定時器序號
timer = null
// 延遲執(zhí)行的情況下,函數(shù)會在延遲函數(shù)中執(zhí)行
// 使用到之前緩存的參數(shù)和上下文
if (!immediate) {
func.apply(context, args)
context = args = null
}
}, wait)
// 這里返回的函數(shù)是每次實際調(diào)用的函數(shù)
return function(...params) {
// 如果沒有創(chuàng)建延遲執(zhí)行函數(shù)(later)饼酿,就創(chuàng)建一個
if (!timer) {
timer = later()
// 如果是立即執(zhí)行运准,調(diào)用函數(shù)
// 否則緩存參數(shù)和調(diào)用上下文
if (immediate) {
func.apply(this, params)
} else {
context = this
args = params
}
// 如果已有延遲執(zhí)行函數(shù)(later),調(diào)用的時候清除原來的并重新設(shè)定一個
// 這樣做延遲函數(shù)會重新計時
} else {
clearTimeout(timer)
timer = later()
}
}
}
/**
* underscore 節(jié)流函數(shù)碰凶,返回函數(shù)連續(xù)調(diào)用時暮芭,func 執(zhí)行頻率限定為 次 / wait
*
* @param {function} func 回調(diào)函數(shù)
* @param {number} wait 表示時間窗口的間隔
* @param {object} options 如果想忽略開始函數(shù)的的調(diào)用,傳入{leading: false}欲低。
* 如果想忽略結(jié)尾函數(shù)的調(diào)用辕宏,傳入{trailing: false}
* 兩者不能共存,否則函數(shù)不能執(zhí)行
* @return {function} 返回客戶調(diào)用函數(shù)
*/
_.throttle = function(func, wait, options) {
var context, args, result;
var timeout = null;
// 之前的時間戳
var previous = 0;
// 如果 options 沒傳則設(shè)為空對象
if (!options) options = {};
// 定時器回調(diào)函數(shù)
var later = function() {
// 如果設(shè)置了 leading砾莱,就將 previous 設(shè)為 0
// 用于下面函數(shù)的第一個 if 判斷
previous = options.leading === false ? 0 : _.now();
// 置空一是為了防止內(nèi)存泄漏瑞筐,二是為了下面的定時器判斷
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
return function() {
// 獲得當(dāng)前時間戳
var now = _.now();
// 首次進(jìn)入前者肯定為 true
// 如果需要第一次不執(zhí)行函數(shù)
// 就將上次時間戳設(shè)為當(dāng)前的
// 這樣在接下來計算 remaining 的值時會大于0
if (!previous && options.leading === false) previous = now;
// 計算剩余時間
var remaining = wait - (now - previous);
context = this;
args = arguments;
// 如果當(dāng)前調(diào)用已經(jīng)大于上次調(diào)用時間 + wait
// 或者用戶手動調(diào)了時間
// 如果設(shè)置了 trailing,只會進(jìn)入這個條件
// 如果沒有設(shè)置 leading恤磷,那么第一次會進(jìn)入這個條件
// 還有一點面哼,你可能會覺得開啟了定時器那么應(yīng)該不會進(jìn)入這個 if 條件了
// 其實還是會進(jìn)入的,因為定時器的延時
// 并不是準(zhǔn)確的時間扫步,很可能你設(shè)置了2秒
// 但是他需要2.2秒才觸發(fā)魔策,這時候就會進(jìn)入這個條件
if (remaining <= 0 || remaining > wait) {
// 如果存在定時器就清理掉否則會調(diào)用二次回調(diào)
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
// 判斷是否設(shè)置了定時器和 trailing
// 沒有的話就開啟一個定時器
// 并且不能不能同時設(shè)置 leading 和 trailing
timeout = setTimeout(later, remaining);
}
return result;
};
};