什么是防抖和节流?

2019/7/19

背景

前端为啥会出现这两个名词?在连续触发事件的场景下,比如监听鼠标移动,用户输入,滚动条滚动,回调会不断连续触发,但是实际情况是不需要连续触发,当然需要获取鼠标位置的场景除外,连续执行回调不仅没必要,而且有性能问题。因此为了解决这种场景下连续触发的问题,才有了防抖和节流。

防抖

设定的延迟时间内,如果事件没有再次被触发,那么执行回调,如果事件再次被触发,那么重新计时。

防抖和节流一般用于事件绑定。

document.addEventListener('scroll', callback)

那么最终应该是返回一个函数(callback),用于绑定事件。大致应该长这样。

function debounce(fn){

    return function() {

        fn()
    }
}

fn 事件触发要执行的代码

上面说了我们需要设定延时,即延时执行 fn。因此需要用上 setTimeout。

function debounce(fn, delay) {

    return function() {

        setTimeout(function() {

            fn()

        }, delay)
    }
}

上面的代码并没有解决事件再次触发,重新计时的问题,因此我们需要将定时器 id 保存下来,再次触发的前清除定时器。

function debounce(fn, delay) {

    var timeoutId = null

    return function() {

        clearTimeout(timeoutId)

        timeoutId = setTimeout(function() {

            fn()

        }, delay)
    }
}

在 setTimeout 中执行,会丢失当前运行时的上下文(this),因此这里不能直接运行,需要做 this 绑定。另外事件回到的参数也丢失了(事件绑定的回调是 debounce 返回的函数,不是 fn),因此在 fn 中不能访问事件回调参数 event 了,因此需要把参数传给 fn。

function debounce(fn, delay) {

    var timeoutId = null

    return function() {

        var context = this

        var args = arguments

        clearTimeout(timeoutId)

        timeoutId = setTimeout(function() {

            fn.apply(context, args)

        }, delay)
    }
}

节流

在设定的时间内,不管事件触发多少次,只认第一次事件触发。不同于防抖,节流不会重新计时,严格按照设定的时间执行回调。代码大致和上面相同,就不按步骤讲解了。

function throttle(fn, delay) {

    var timeoutId = null

    return function() {

        var context = this

        var args = arguments

        if (!timeoutId) {

            timeoutId = setTimeout(function() {

                fn.apply(context, args)

                timeoutId = null

            }, delay)
        }

    }
}

参考

https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/5

原创文章,持续完善中,转载请注明出处。本文地址: https://www.qinshenxue.com/article/what-is-debounce-and-throttle.html