防抖与节流:大厂面试常考点,学会它让你有备无患

226 阅读6分钟

引入

今天让我们一起学习两个在网页开发中常用的技术———防抖与节流,这也是许多大厂面试时的常考题。下面,我将分别向大家介绍这两项技术背后的底层代码。

防抖

防抖(debounce)技术主要用于处理频繁触发的事件,以避免不必要的函数执行和性能浪费。

它的主要思想是:在事件被第一次触发后,延迟一段时间再执行相应的函数。如果在延迟期间内又有相同的事件被触发,则重新开始计时,直到最后一次触发后的延迟时间结束,才开始执行函数。

举例

例如用百度搜索时,我们每次输入内容时都会给我们一些搜索建议:

屏幕截图 2024-07-02 105334.png 对于这个输入框的输入事件,如果直接监听该事件并执行相关函数,可能会在用户快速输入时频繁触发函数。而通过防抖,可以确保用户停止输入一段时间后,才执行实际的操作,避免每次输入都触发搜索建议。

防抖函数的代码实现

在防抖函数中,会接收两个参数:一个是需要防抖的函数fn,另一个是延迟时间delay,并且会返回一个函数function

//防抖函数
   function debounce(func, delay) {
    let timeout;
    return function(...args) {
        clearTimeout(timeout);
        timeout = setTimeout(() => 
        func.apply(this, args), 
        delay);
    };
}

//防抖函数的使用

function windowSize() {
   console.log('size changed');
}

const debouncedWindowSize = debounce(windowSize, 1000);

window.addEventListener('resize', debouncedWindowSize);

在防抖函数debounce中声明的闭包变量timeout是用来存储返回的定时器的id,这样下一次调用时就可以实现清除计时器的效果。

返回的function函数在每一次触发事件时会被调用,它首先会清除之前调用的定时器(1号,如果有的话),然后设置一个新的定时器(2号),并在延迟dalay毫秒后执行func函数(需要防抖的函数),如果在延迟期间该事件又触发了,那么又会清除刚刚设置的定时器(2号),并继续设置一个新定时器(3号)。直到一个完整的延迟时间结束,才会执行一次func函数。

节流

节流也是前端开发中常用到的一种性能优化技术,主要用来控制函数被调用的频率,防止函数在短时间内被过度调用

它的主要思想是:在设定的时间间隔内,函数最多只执行一次。如果在这个时间间隔内函数被多次调用,只有第一次调用会被立即执行,其余的调用会被忽略,直到下一个时间间隔开始。

举例

例如输入框的实时搜索功能,当用户在输入框中输入内容时,实时进行搜索请求可能过于频繁。通过节流函数,可以每隔一段时间(例如 500 毫秒)才发送一次搜索请求,既保证了搜索的及时性,又减少了不必要的网络请求。

节流函数的代码实现

这里我们就以输入框为例,截取HTML中的一部分代码如下:

<div class="row">
        <div>
            节流后的input <input type="text" id="input" />
        </div>
    </div>
    <script>
        const input = document.getElementById('input');
        const ajax = (content) => {
            console.log(`ajax request ${content}`);
        }
        //throttle是节流功能函数
        const throttle = (func,delay ) => {
            let last, deferTimer;//两个闭包中的自由变量
            return (...args) => {
                let now = +new Date();
                if(last && now - last < delay){
                    clearTimeout(deferTimer)
                    deferTimer = setTimeout(() => {
                        last = now;
                        func(...args)
                    }, delay);
                }else{
                    last = now;
                    func(...args);
                }
            }
        }
        
        let throttleFunc = throttle(ajax,3000);
        input.addEventListener('keyup',(e) =>{
            let value = e.target.value;
            throttleFunc(value);
        })
    </script>

这里我们在页面上加入一个输入框,并通过document.getElementById()方法获取输入框中的元素,要注意的是:第8行代码并不是真的发送一个Ajax请求,只是定义了一个名为ajax的函数用来打印收到的参数content

我们重点看节流函数throttle,它接收两个参数:func表示需要被节流的函数,delay表示最小时间间隔,return返回的是一个新的函数,这个函数是实际绑定到事件上的处理函数,它会根据delay来控制func的执行。

我们定义了两个变量lastdeferTimer,分别用来存储上一次事件执行的时间定时器Id

在返回的函数中,先获取当前的毫秒级时间,再用if循环进行判断:如果事件被执行过并且上一次执行到现在还不足delay时间,则需要节流操作,接着就会先清除之前的定时器(如果有的话),并设置一个新的定时器:将现在的时间赋值给last在延迟delay毫秒后执行函数func;否则(已经经过delay时间)就进入else语句,执行func函数并将现在的时间赋值给last

因为节流函数会返回一个新的事件处理函数,所以在29行我们需要用一个函数来接收,然后才可以进行调用。

总结

以上就是关于防抖与节流的介绍举例和代码实现,看到这里相信大家都能看出来防抖和节流其实有点像的,现在为大家总结一下这两项技术:

原理

防抖的主要原理是:在事件被第一次触发后,延迟一段时间再执行相应的函数。如果在延迟期间内又有相同的事件被触发,则重新开始计时,直到最后一次触发后的延迟时间结束,才开始执行函数。它确保函数在一系列连续的调用中只执行一次。

节流的主要原理是:在设定的时间间隔内,函数最多只执行一次。如果在这个时间间隔内函数被多次调用,只有第一次调用会被立即执行,其余的调用会被忽略,直到下一个时间间隔开始。它限制函数在一定时间间隔内的执行次数。

执行时机:

防抖是在一系列连续调用结束后的一段延迟时间后执行,而节流是在固定的时间间隔内执行。

使用场景

根据它们原理的不同就可以知道,防抖适合于需要在用户完成一系列操作后才执行的场景,如输入框的实时搜索、窗口大小调整后重新布局等。而节流适用于需要对用户操作做出响应但又不想过于频繁的执行的场景,如滚动事件的处理、对鼠标移动的跟踪、连续点击按钮等。