节流
节流:如果你持续触发事件,每隔一段时间,只执行一次事件
关于节流的实现,有两种主流的实现方式,一种是使用时间戳的方式,一种是设置定时器
使用时间戳
实现思路为:当触发条件的时候,我们取出当前的时间戳,然后减去之前的时间戳(初始化为0),如果大于设定的时间周期,就执行函数,然后更新时间戳为当前的时间戳,如果小于,则不执行
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
}
}
}
使用定时器
实现思路为:当触发事件的时候,我们设置一个定时器,再次触发事件的时候,如果定时器存在,就不执行,直到定时器执行,然后执行函数,清空定时器,再设置下一个定时器
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)
}
}
}
比较两种方法:
- 第一种事件会立即执行,第二种事件会在n秒后第一次执行
- 第一种事件停止触发后没有办法再执行事件,第二种事件停止触发后依然会再执行一次事件
双剑合璧
鼠标移入能立即执行,停止触发的时候还能再执行一次
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()
// 下次触发func剩余时间
var remaining = wait - (now - previous)
context = this
args = arguments
// 如果没有剩余时间了或者你改了系统时间
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
}
优化
我们可增加一个参数options来约定:
- leading:false表示禁用第一次执行
- trailing:false表示禁用停止触发的回调
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
// 下次触发func剩余时间
var remaining = wait - (now - previous)
context = this
args = arguments
// 如果没有剩余时间了或者你改了系统时间
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
}
注意
如果同时设置 leading:false和trailing:false,比如当你鼠标移出的时候,因为trailing设置为false,停止触发的时候不会设置定时器,所以只要再过了设置的时间,再移入的话,就会立即执行,就违反了leading:false,所以throttle只有三种用法:
throttle(func, 1000)
throttle(func, 1000, {leading:false})
throttle(func, 1000, {trailing:false})