js的節流和防抖
1,節流
節流就是對連續的函式觸發,在設定的間隔時間段內,只讓其執行一次。
先來看看js高階程式設計3裡面節流的程式碼
function throttle (method, context, wait) { clearTimeout(method.tId) method.tId = setTimeout(function () { method.call(context) }, wait) }
當函式連續執行的時候,如果之前的定時器還沒執行,就把它清除了,再從新設定一個新的定時器。
我們可以對這個進行改進
function throttle (fn, wait) {
let timeout;
let prevTime = 0;
return (...args) => { //返回一個函式
let now = new Date;
let remaining = wait - (now - prevTime) // 下一次執行的時間,
if (remaining <=0 || remaining > wait) { // 如果下一次的時間小於等於0,立刻執行一次函式,並把時間戳改為當前的時間。
clearTimeout(timeout)
timeout = null
prevTime = now
fn(args)
} else if (!timeout) { // 如果當前沒有定時器 那麼就新加一個定時器。
timeout = setTimeout(() => {
timeout = null;
prevTime = new Date;
fn(args)
}, remaining)
}
}
}
第一次執行: timeout為undefined, prevTime為0 remaining為負數,走if的第一個,立即執行了函式並將下次時間改成當前時間
第二次執行: 下一次時間為正,定時器還是為undefined,走if的第二個,設定定時器
下一次執行(不超過wait時間內) : remaining大於0,定時器為true,所以直接跳出
understore裡面的節流考慮了兩個高階的配置: 是否在第一次觸發的時候就執行函式 和 最後一次的定時器觸發的執行函式。還提供了節流的取消函式。
function throttle(fn, wait, option = {}) { let timeout; let prevTime = 0 let throttled = (...args) => { let now = new Date; if (!prevTime && option.leading === false) prevTime = now; let remaining = wait - (now - prevTime); if (remaining <= 0 || remaining > wait) { if (timeout) { clearTimeout(timeout) timeout = null; } prevTime = now fn(args) } else if (!timeout && option.trailing !== false) { timeout = setTimeout(() => { prevTime = option.leading === false ? 0 : new Date; fn(args) }) } } throttled.cancel = () => { clearTimeout(timeout); prevTime = 0; timeout = null; } return throttled }
除了加了兩個配置項和取消函式,別的基本都是一樣的。
需要注意的是,首次不執行和最後不執行這兩個不能同時配置,只能配置一個。
2,防抖
其實我感覺防抖和節流差別不大,主要的差別在於: 在wait的時間內,反覆觸發函式的話,節流不會理會這些,而防抖的話,只要你觸發了,他就會清除之前的定時器,從新設定一個定時器。
比如說坐電梯,如果是節流的話,電梯等待時間為5S,從第一個人進電梯開始算,到5S後他就會關門執行。
如果是防抖的話,電梯等待時間為5S,在這5S之內,如果有人進電梯,他就會重新計時,只有在5S中沒有人進電梯了,電梯才關門執行。
function debounce (fn, wait) {
let timeout;
return (...args) => {
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => {
fn(args)
}, wait)
}
}
返回一個函式, 先清除之前的定時器,然後再新加定時器。
underscore裡面的防抖添加了一個高階配置,是否立即執行一次函式。
function debounce(fn, wait, immediate) { let timeout; return (...args) => { let callNow = immediate && !timeout; if (timeout) clearTimeout(timeout); timeout = setTimeout(() => { timeout = null; if (!immediate) fn(args) }, wait) if (callNow) fn(args) } }
這裡添加了immediate這個配置項,如果為true的話,那麼觸發第一次的時候就執行了要執行的函式,定時器裡面不執行。