【前端性能优化】JS节流(throttle)和防抖(debounce)的理解、实现和总结

版权声明:本文为博主原创文章,未经博主允许不得转载 https://blog.csdn.net/AiHuanhuan110/article/details/89225819

节流

理解

​   节流,就是拧紧水龙头让水少流一点,但是不是不让水流了。想象一下在现实生活中有时候我们需要接一桶水,接水的同时不想一直站在那等着,可能要离开一会去干一点别的事请,让水差不多流满一桶水的时候再回来,这个时候,不能把水龙头开的太大,不然还没回来水就已经满了,浪费了好多水,这时候就需要节流,让自己回来的时候水差不多满了。

​   在JS里典型的场景就是,图片懒加载监听页面的scoll事件,或者监听鼠标的mousemove事件。这些事件对应的处理方法相当于水,由于scroll和mousemove在鼠标移动的时候会被浏览器频繁的触发,会导致对应的事件也会被频繁的触发(水流的太快了),这样就会造成很大的浏览器资源开销,而且好多中间的处理是不必要的,这样就会造成浏览器卡顿的现象,影响性能。

​   这时候就需要节流,如何节流呢?我们无法做到让浏览器不去触发对应的事件,但是可以做到让处理事件的方法执行频率减少,从而减少对应的处理开销。

图示:

持续触发scroll事件,但每1000ms执行一次函数
节流原理图示

JS实现

/** 实现思路:
    **  参数需要一个执行的频率,和一个对应的处理函数,
    **  内部需要一个lastTime 变量记录上一次执行的时间
    **/
function throttle (func, wait) {
    let lastTime = null
    // 为了避免每次调用lastTime都被清空,利用js的闭包返回一个function确保不声明全局变量也可以
    return function () {
        let now = Date.now()
        // 如果上次执行的时间和这次触发的时间大于一个执行周期,则执行
        if (now - lastTime - wait > 0) {
            func()
            lastTime = now
        }
    }
}

调用:

function handle(){
	console.log(123)
}

window.addEventListener('scroll', throttle(handle, 1000))

​   这时候疯狂的滚动页面,会发现会1000ms打印一个123,而没有节流的话会不断地打印, 可以改变wait参数去感受下不同。

​   但是到这里,我们的节流方法是不完善的,因为我们的方法没有获取事件发生时的this对象,而且由于我们的方法简单粗暴的通过判断这次触发的时间和上次执行时间的间隔来决定是否执行回调,这样就会造成最后一次触发无法执行,或者用户出发的间隔确实很短,也无法执行,造成了误杀,所以需要对方法进行完善。

function throttle (func, wait) {
    let lastTime = null
    let timeout = null
    return function () {
        let context = this
        let now = Date.now()
        // 如果上次执行的时间和这次触发的时间大于一个执行周期,则执行
        if (now - lastTime - wait > 0) {
            // 如果之前有了定时任务则清除
            if (timeout) {
                clearTimeout(timeout)
                timeout = null
            }
            func.apply(context, arguments)
            // 这里lastTime是上次的触发时间
            lastTime = now
        } else if (!timeout) {
            timeout = setTimeout(() => {
                // 改变执行上下文环境
                func.apply(context, arguments)
            }, wait)
        }
    }
}

这样我们的方法就相对完善了,调用方法和之前相同。

防抖

理解

​   在页面里,有这种情况,假设我们的一个输入框,输入内容的同时可能会去后台查询对应的联想 词,如果用户输入的同时,频繁的触发input事件,然后频繁的向后台发送请求,那么直到用户输入完成时,之前的请求都应该是多余的,假设网络慢一点,后台返回的数据比较慢,那么显示的联想词可能会出现频繁的变换,直到最后的一个请求返回。这个时候就可以在一定时间内监听是否再次输入,如果没有再次输入则认为本次输入完成,发送请求,否则就是判定用户仍在输入,不发送请求。

图示:

持续触发scroll事件并不执行handle函数,只有在停止触发scroll事件且间隔1000ms后,才执行一次函数。再次触发scroll事件,也是如此。防抖原理图示

JS实现

防抖的方法,和节流思路一致,但是只有在抖动被判定结束后,方法才会得到执行。

function debounce(func, wait) {
    let timeout = null;
    return function() {
        if(timeout !== null){
            clearTimeout(timeout);
        }
        timeout = setTimeout(func, wait);
    }
}

调用:

function handle(){
	console.log(123)
}

window.addEventListener('scroll', debounce(handle, 1000))

这时候按照之前同样的方式调用,会发现无论怎么疯狂的滚动窗口,只有停止滚动后1000ms,才会执行对应的事件。

总结

  • 防抖和节流有很多实现的方法,其大致思路基本是这样的。
  • 防抖和节流是不同的,因为节流虽然中间的处理函数被限制了,但是只是减少了频率,而防抖则把中间的处理函数全部过滤掉了,只执行规判定时间内的最后一个事件。
  • debounce和throttle 各有特点,在不同的场景要根据需求合理的选择策略。如果事件触发是高频但是有停顿时,可以选择debounce; 在事件连续不断高频触发时,只能选择throttle,因为debounce可能会导致动作只被执行一次。

猜你喜欢

转载自blog.csdn.net/AiHuanhuan110/article/details/89225819