節(jié)流
節(jié)流:如果你持續(xù)觸發(fā)事件,每隔一段時(shí)間,只執(zhí)行一次事件
關(guān)于節(jié)流的實(shí)現(xiàn),有兩種主流的實(shí)現(xiàn)方式,一種是使用時(shí)間戳的方式,一種是設(shè)置定時(shí)器
使用時(shí)間戳
實(shí)現(xiàn)思路為:當(dāng)觸發(fā)條件的時(shí)候,我們?nèi)〕霎?dāng)前的時(shí)間戳,然后減去之前的時(shí)間戳(初始化為0),如果大于設(shè)定的時(shí)間周期,就執(zhí)行函數(shù),然后更新時(shí)間戳為當(dāng)前的時(shí)間戳,如果小于,則不執(zhí)行
function throttle(func, wait) {
var context, args
var previous = 0
return function() {
var now = +new Date()
context = this
args = arguments
if(now - previous > wait) {
func.apply(context, args)
previous = now
}
}
}
使用定時(shí)器
實(shí)現(xiàn)思路為:當(dāng)觸發(fā)事件的時(shí)候,我們?cè)O(shè)置一個(gè)定時(shí)器,再次觸發(fā)事件的時(shí)候,如果定時(shí)器存在,就不執(zhí)行,直到定時(shí)器執(zhí)行,然后執(zhí)行函數(shù),清空定時(shí)器,再設(shè)置下一個(gè)定時(shí)器
function throttle(func, wait) {
var timeout
var context, args
return function() {
context = this
args = arguments
if(!timeout) {
timeout = setTimeout(function() {
timeout = null
func.apply(context, args)
}, wait)
}
}
}
比較兩種方法:
- 第一種事件會(huì)立即執(zhí)行,第二種事件會(huì)在n秒后第一次執(zhí)行
- 第一種事件停止觸發(fā)后沒(méi)有辦法再執(zhí)行事件,第二種事件停止觸發(fā)后依然會(huì)再執(zhí)行一次事件
雙劍合璧
鼠標(biāo)移入能立即執(zhí)行,停止觸發(fā)的時(shí)候還能再執(zhí)行一次
function throttle(func, wait) {
var timeout, context, args, reslut
var previous = 0
var later = function() {
previous = +new Date()
timeout = null
func.apply(context, args)
}
var throttled = function() {
var now = +new Date()
// 下次觸發(fā)func剩余時(shí)間
var remaining = wait - (now - previous)
context = this
args = arguments
// 如果沒(méi)有剩余時(shí)間了或者你改了系統(tǒng)時(shí)間
if(remaining <=0 || remaining > wait) {
if(timeout) {
clearTimeout(timeout)
timeout = null
}
previous = now
func.apply(context, args)
} else if(!timeout) {
timeout = setTimeout(later, remaining)
}
}
return throttled
}
優(yōu)化
我們可增加一個(gè)參數(shù)options來(lái)約定:
- leading:false表示禁用第一次執(zhí)行
- trailing:false表示禁用停止觸發(fā)的回調(diào)
function throttle(func, wait, options) {
var timeout, context, args, reslut
var previous = 0
if(!options) options = {}
var later = function() {
previous = options.leading === false ? 0 : new Date().getTime()
timeout = null
func.apply(context, args)
if(!timeout) context = args = null
}
var throttled = function() {
var now = new Date().getTime()
if(!previous && options.leading === false) previous = now
// 下次觸發(fā)func剩余時(shí)間
var remaining = wait - (now - previous)
context = this
args = arguments
// 如果沒(méi)有剩余時(shí)間了或者你改了系統(tǒng)時(shí)間
if(remaining <=0 || remaining > wait) {
if(timeout) {
clearTimeout(timeout)
timeout = null
}
previous = now
func.apply(context, args)
if(!timeout) context = args = null
} else if(!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining)
}
}
// 取消功能
throttled.cancel = function() {
clearTimeout(timeout)
previous = 0
timeout = null
}
return throttled
}
注意
如果同時(shí)設(shè)置 leading:false和trailing:false,比如當(dāng)你鼠標(biāo)移出的時(shí)候,因?yàn)閠railing設(shè)置為false,停止觸發(fā)的時(shí)候不會(huì)設(shè)置定時(shí)器,所以只要再過(guò)了設(shè)置的時(shí)間,再移入的話,就會(huì)立即執(zhí)行,就違反了leading:false,所以throttle只有三種用法:
throttle(func, 1000)
throttle(func, 1000, {leading:false})
throttle(func, 1000, {trailing:false})