一首歌的时间学🎶节流与防抖

253 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情

☀ 前言


在很多小伙伴的印象中 节流与防抖 似乎只有在面试过程中才能见到,其实并非如此。在我们的日常开发过程中,也常常可以用到它们 来优化项目性能。

那么接下来我们来从 日常 开发角度,来学习 节流与防抖

🎉 防抖


💕 场景再现

我们不谈概念,毕竟概念实在让人疯狂。我们就举个生活中的例子。

小明是一位码农,每天都要坐电梯上下班,并且小明暗恋着同层的一位女神, 于是小明每天都会比女神提前一分钟出发,为女神占电梯。

假设 当小明 按下电梯键后,电梯门打开后会在3秒后默认关闭。 于是小明在等待女神的期间, 会不停的按击电梯键。

我们设想一个场景,假设小明连续 按击 电梯键 2次, 那么电梯门会在3秒后连续关闭 2次么?

我想不会,至少我没见过。我相信在座的各位和小明都没见过这种电梯。 就这样在小明的不断努力下,女神坐上了电梯,并友好的对着小明唱了一首 听我说谢谢你

image.png

💕延伸

上面的场景就是一个 防抖 的例子。 有2个关键点

  • 3秒钟(单位时间)
  • 电梯关门 (执行的事件)

ps: 有杠精小伙伴问电梯开门咋被忽略了,这个么 emmm

其实防抖可以简单的理解为: 单位时间内事件多次触发会被重置

💕实际场景

小明送完女神,刚坐到办公位,就受到了 夺命连环call, 说是一个用户 一秒钟点击 提交按钮 100次,导致产生的 100条记录。 小明听到 惊呆了, 只能默默的写了下面的代码.

<button onclick="sendInfo()">点击发送</button>

👣 步骤1: 编写 sendInfo 函数, 并连续点击 10次

function sendInfo () {
    console.log('点击一下')
}

于是 点击十次 * 10

image.png

👣步骤2: 添加定时器控制时间, 并连续点击 10次

function debounce (fn, wait) {
      setTimeout(()=> {
         fn()
     }, wait)
}
    
    function sendInfo() {
       debounce(() => {
           console.log('点击一次')
       }, 200)
    }

然后小明发现并没有什么效果, 只是延迟了 200ms, 思索了一下.

👣步骤3: 清空定时器, 保证单位时间只执行一次

function debounce (fn, wait) {
      let timer;
      clearTimeout(timer);
      timer = setTimeout(()=> {
         fn()
     }, wait)
    }
    
    function sendInfo() {
       debounce(() => {
           console.log('点击一次')
       }, 200)
    }

结果发现 清空了,又好像没有清空。原因很简单: 每次执行 debounce 都会新建 timer变量, 老的

👣步骤4: 最后一步,请叫我 小明神

function debounce (fn, wait) {
        let timer;
        return function () {
            clearTimeout(timer)
            timer = setTimeout(()=> {
                fn()
            }, wait)
        }
    }

    const db = debounce(() => {
        console.log("点击了一下")
    }, 200)

    function sendInfo() {
        db()
    }

这里可以很清晰的看到, debounce 返回一个函数, 此时 timer 变量 处于 debounce 的函数作用域中。所以在 db每一次 执行过程中, timer都是同一个。

🎉 小结

这里是一个最简易的防抖函数,在日常工作中,可能有很多变种。例如立即执行等等, 就好比 小明送女神礼物, 一束装扮精美的花。 但是再精美 它还是一束花 !而在我们日常开发中, 也有很多场景可以用到防抖如: 输入框输入发送请求,窗口移动等

🎉 节流


💕 场景再现

老样子, 我们不谈概念。直接 编个故事

小明无意间知道了女神爱养🐟, 为了能和女神套进关系。小明将养🐟列为 人生 第一小目标。 为了能够让鱼儿快快长大。 小明发明了喂食神器, 每 30秒 投喂一次。 可是好景不长😂, 鱼儿全死光了。由于设计缺陷,每30秒投喂的功能无法改变 (如mousemove的频率无法修改一样), 小明只能再次发明了 喂食挡板,将投喂的食物进行阻挡(如代码中阻止事件执行)。 小明并将此发明 分享给了女神,于是女神友好的对着小明唱了一首 听我说谢谢你

💕延伸

上面的场景就是一个 节流 的例子。 有个关键点

  • 单位时间只执行一次(如喂鱼)

其实节流可以简单的理解为: 单位时间内事件只能触发一次

💕实际场景

小明接到一个需求, 记录用户的鼠标移动热点图, 要求5秒钟记录一次。

💕代码实现

小明回想到了前一天写的防抖函数,变得犹如神助, 于是分分钟写出来期望中得代码。

    function throttle(fn, wait) {
        let oTime = 0;
        return function (...arg) {
            let nTime = new Date();
            if (nTime - oTime > wait) {
                fn(...arg);
                oTime = nTime;
            }
        }
    }

    function pushPosition(e) {
        const {pageX, pageY} = e;
        console.log(`记录坐标x:${pageX} y:${pageY}`)
    }

    const db = new throttle(pushPosition, 5000)

    document.addEventListener('mousemove', (e) => {
        db(e)
    })

我们可以看到和防抖函数一样, 具体关键点是触发时间。 只不过唯一的区别, 节流会在单位时间的触发一次,而不会重置。

🎉 小结

节流在日常开发中使用场景也有很多: 例如 鼠标移动的节流, 鼠标滚动的节流等等

🌹 总结

以上是个人较为浅显的理解,如有不对的地方,请指出😊。

  • 防抖: 单位时间内事件多次触发会被重置 适用场景: 按钮的重复点击, 输入框输入发送请求
  • 节流: 单位时间内事件只能触发一次 适用场景: 鼠标移动的节流, 鼠标滚动的节流

当然,日常工作中,有很多优秀的工具提供了相应的方法,它们更优秀,更强壮。我们可以直接使用它,但是 我们还是需要理解它。 知己知彼百战不殆