【面试题集 —— No.04】函数防抖(debounce)和函数节流(throttle)的原理解析,适用场景及源码实现


1. 事件高频触发的场景

在JavaScript编程中,如果某个事件在短时间内被高频率的触发,可能就会出现对应的回调函数响应速度跟不上其调用频率的问题,进而会导致页面延迟,假死或卡顿的现象。这样会极大的浪费系统资源,同时糟糕的UI带来的用户体验也是很差的。

函数防抖(debounce)和函数节流(throttle)就是我们在这类场景下常用的优化手段,从而达到节约系统资源,改善用户体验的目的。

2. 原理解析

函数防抖(debounce)和函数节流(throttle)技术最终都是要限制(减少)函数的执行频次。具体的实现原理也很简单:巧妙地使用 setTimeout 来存放待执行的函数,这样可以很方便的利用 clearTimeout 在合适的时机来清除待执行的函数。

  • 函数防抖(debounce) —— 一定时间内,只会执行最后一次函数回调;
  • 函数节流(throttle) —— 一定时间内,只执行一次函数回调;

3. 函数防抖的适用场景举例 —— 搜索查询

函数防抖(debounce)的完整描述为:高频事件触发后,经过一定的时间间隔才会执行函数回调;如果事件在时间间隔内又被触发,则会重新计时,即只会执行最后一次的函数回调。

最常见的场景就是搜索查询。如果不做防抖的话,用户每输入一个字符就会发送一次请求,势必会给服务器造成不小的压力,进而影响系统性能。比如我们简单模拟一下:

//HTML
<input type="text">

//JavaScript
<script>
    window.onload = function () {
        function inputTap(e) {
            console.log(e.target.value)
        }
        document.addEventListener('input', inputTap)
    }
</script>

在这里插入图片描述
此时我们通过给input输入框绑定inputTap事件并监听其输入,可以看到,每次我们输入一个字符,都触发了inputTap事件。为了改善优化这一场景,就可以使用防抖技术,简单封装一个防抖函数:

具体原理是使用到了setTimeout定时器函数,在定时器函数timer定义之前加一步clearTimeout清除定时器的操作。

/**
* @param {*} fn 回调函数
* @param {*} delay 延迟时间
*/
const debounce = function (fn, delay) {
    let timer = null;
    return function () {
        clearTimeout(timer);
        timer = setTimeout(() => {
            fn.apply(this, arguments)
        }, delay);
    };
};

这样每次事件触发后,虽然都定义了定时器函数timer,但是如果下次操作来的比较快,上次定义的timer还没来得及执行就被清除了;只有当两次操作的时间间隔大于设定值时,timer才能正常执行。

比如我们设定用户完成输入操作后,间隔1000毫秒再调用inputTap函数:

function inputTap(e) {
    console.log(e.target.value)
}
document.addEventListener('input',debounce(inputTap,1000))

在这里插入图片描述

4. 函数节流的适用场景举例 —— 长列表滑动刷新

函数节流(throttle)就是指连续触发事件但是在一定时间内只执行一次函数,节流会稀释函数的执行频率。对于节流技术,一般有两种方式可以实现:时间戳版和定时器版

这里我们就以长列表滑动刷新的场景为例:监听页面滚动并持续输出一个逐渐累加的num值。

//HTML
<div class="box" style="background: tomato;height: 700px;"></div>
<div class="box" style="background: skyblue;height: 700px;"></div>
<div class="box" style="background: red;height: 700px;"></div>
<div class="box" style="background: yellow;height: 700px;"></div>
<div class="box" style="background: yellowgreen;height: 700px;"></div>

//JavaScript
<script>
    window.onload = function () {
        let num = 0
        function scrollTap() {
            num++
            console.log(`num: ${num}`)
            return num
        }
        document.addEventListener('scroll', scrollTap)
    }
</script>

在这里插入图片描述

这样的页面滚动刷新很有可能会造成页面的卡顿,进而影响性能。所以就可以使用函数节流(throttle)的技术来改善优化。

① 时间戳版的节流函数:

原理就是分别记录两次操作的时间戳,并判断两者的时间间隔是否大于设定值,只有大于设定值时才会调用回调函数。

const throttle = function (fn, delay) {
    let preTime = Date.now()
    return function () {
        let doTime = Date.now()
        if (doTime - preTime >= delay) {
            fn.apply(this, arguments)
            preTime = Date.now()
        }
    }
}

② 定时器版的节流函数:

原理是使用了setTimeout来定义了一个计时器函数timertimer每次执行完都会清除自身(timer = null),这样下次才能进入if分支,重新定义timer函数来调用回调函数。

const throttle = function (fn, delay) {
    let timer = null
    return function () {
        if (!timer) {
            timer = setTimeout(() => {
                fn.apply(this, arguments)
                clearTimeout(timer)
                timer = null // 敲重点
            }, delay);
        }
    }
}

当然两种方法达到的效果是一样的,可以明显的对比出:这次输出的数值就少的多了。从而实现了减少回调函数的执行次数的目的。

在这里插入图片描述


参考文章:https://blog.csdn.net/qq_36818627/article/details/108023381

每一个不曾起舞的日子,都是对生命的辜负!

—— 尼采

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值