优化篇:防抖、节流

634 阅读4分钟

节流

解决的问题:

假设有一个事件会频繁触发,比如鼠标移动的时候会频繁鼠标的移动事件,这会消耗太多性能。要解决这个问题,你可以通过节流的方式,控制触发的间隔,从而进行性能的优化。

也就是说:节流就是同一个函数被事件触发之后执行,必须间隔一段时间。事件可以触发很多次,但是执行的频率是被控制的。

实现目标:实现一个函数,这个函数的参数有两个:一个是将要执行的方法fn;第二个是这个fn间隔多久才执行一次,也就是延迟时间delay(单位是ms)。

实现思路:执行fn的时候,查看是否有计时器,如果有那么就不执行fn(因为时间到了只会,fn会自动触发),如果没有计时器,那么就添加一个计时器并把时间设置为delay的值

//这个函数传入一个函数fn,以及一个延迟执行的时间(默认是100毫秒),实现目标:fn函数要在100ms的间隔才触发一次。
//思路:执行fn的时候,查看是否有计时器,如果有那么就不执行fn(因为时间到了只会,fn会自动触发),如果没有计时器,那么就添加一个计时器并把时间设置为delay的值。
function throttle(fn, delay = 100) {
    let timer = null
    return function(){
        if(timer) {
            return
        }
        timer = setTimeout(() => {
            //this指向的是div对象,而arguments指向是函数本身的类数组类型的参数列表,而函数本身由事件监听器调用,所以arguments[0]存的是事件对象。
            fn.apply(this, arguments)
            timer = null
        },delay)
    }
}
​
let div = document.querySelector('.throttle')
div.addEventListener('click', throttle(function(){}),200)

如果获取了一个节点div,并通过添加事件监听器的方式来添加节流函数,注意this始终指向调用该事件处理器的节点本身

防抖

防抖,意思是每当事件触发,处理函数就会在间隔触发点之后的一段时间过后才会执行。 假定这段时间被指定为200毫秒,那么处理函数就会在200毫秒之后才执行。如果这段时间内又触发了事件,那么间隔时间将会从新的事件之后开始计算。

解决的问题:假设有一个事件会频繁触发,但是只关注最后的结果,那么应该使用防抖的方式,尽量去获取最后的结果,而不对中间值进行任何处理。比如文本输入框里的内容完成之后要进行内容检索,但是在完成之前,内容总是在不停的变化,此时就适合使用防抖来进行优化。

实现目标:实现一个函数,这个函数的参数有两个:一个是触发事件时,将要执行的方法fn;第二个是这个fn在多久之后才能执行,也就是延迟时间,取名delay(单位是ms)

实现思路:由于不关注中间结果,只关注最后的状态,所以在delay规定的时间200毫秒,在200毫秒的过程中,如果触发了事件,那么就一直移除定时器,然后再添加一个新的定时器,由于定时器规定的时间一直不到达,那么fn就一直不会被执行。直到不再触发事件为止。


跟节流的思路类似。区别在于:节流的思路是,发现定时器那就不执行fn,直到定时器时间到了执行完fn,只会在再次触发的时候再添加新的定时器。而防抖的思路是:在200ms过程中,触发事件 -> 发现有定时器 -> 去除定时器,再添加一个新的定时器。除非在delay时间过程中,没有触发事件 -> 无法发现有定时器 -> 无法去除定时器 -> 成功执行fn。

function debounce(fn, delay = 200){
                let timer = null
                return function(){
                    if(timer) {
                        clearTimeout(timer)
                    }
                    timer = setTimeout(() => {
                        fn.apply(this, arguments)
                        timer = null
                    }, delay)
                }
            }
            
            let inputName = document.querySelector('#username')
            inputName.addEventListener('keyup', debounce(function(){
                console.log(this.value)
            }))