再学JS--事件节流

节流

节流:如果你持续触发事件,每隔一段时间,只执行一次事件

关于节流的实现,有两种主流的实现方式,一种是使用时间戳的方式,一种是设置定时器

使用时间戳

实现思路为:当触发条件的时候,我们取出当前的时间戳,然后减去之前的时间戳(初始化为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)
        }
    }
}

比较两种方法:

  1. 第一种事件会立即执行,第二种事件会在n秒后第一次执行
  2. 第一种事件停止触发后没有办法再执行事件,第二种事件停止触发后依然会再执行一次事件

双剑合璧

鼠标移入能立即执行,停止触发的时候还能再执行一次

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来约定:

  1. leading:false表示禁用第一次执行
  2. 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:falsetrailing:false,比如当你鼠标移出的时候,因为trailing设置为false,停止触发的时候不会设置定时器,所以只要再过了设置的时间,再移入的话,就会立即执行,就违反了leading:false,所以throttle只有三种用法:

throttle(func, 1000)
throttle(func, 1000, {leading:false})
throttle(func, 1000, {trailing:false})

你可能感兴趣的:(再学JS--事件节流)