事件的防抖与节流(进阶必备知识)

291 阅读3分钟

这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战

前言

在平时的编码过程中,当绐浏览器注册一个事件,经常会遇到一些执行次数非常频繁的事件比如:

scrollresize 事件、鼠标事件(mouseover、mousemove等)、键盘事件(keyup、keydown等)。事件频繁的执行会导致浏览器进行大量的计算而引发页面卡顿假死的情况,为些我们需要通过一些手段来解决这个问题,所以就有了防抖(Debounce)和节流(throttle)这两个技术。

事件节流和事件防抖的考察频率,随着前端性能近年来愈发受到重视而与日俱增。

事件节流和事件防抖的本质都是以闭包的形式存在。它们通过对事件的回调函数进行包裹、以保存自由变量的形式来缓存时间信息,最后使用 setTimeout 来控制事件的触发频率。

Throttle节流

TIP 👉 第一个人说了算,throttle的中心思想在于:在某段时间内,不管你触发了多少次回调,我都只认第一次,并在计时结束时给予响应。

总结下来,所谓的“节流”,是通过在一段时间内无视后来产生的回调请求来实现的

理解了大致的思路,我们现在一起实现一个 throttle:

// fn是我们需要包装的事件回调, interval是时间间隔的阈值
function throttle(fn, interval) {
    // last为上一次触发回调的时间let last = 0
    // 将throttle处理结果当作函数返回
    return function () {
        // 保留调用时的this上下文
        let context = this
        // 保留调用时传入的参数
        let args = arguments
        // 记录本次触发回调的时间
        let now = +new Date()
        // 判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值
        if (now - last >= interval) {
            // 如果时间间隔大于我们设定的时间间隔阈值,则执行回调
            last = now; fn.apply(context, args);
        }
    }
}
// 用throttle来包装scroll的回调
const better_scroll = throttle(() => console.log('触发了滚动事件'), 1000) document.addEventListener('scroll', better_scroll)

Debounce防抖

TIP 👉 最后一个人说了算,防抖的中心思想在于:我会等你到底。在某段时间内,不管你触发了多少次回调,我都只认最后一次 类似英雄联盟的回城效果。

我们基于上面的理解,一起来写一个 debounce:

// fn是我们需要包装的事件回调, delay是每次推迟执行的等待时间
function debounce(fn, delay) {
    // 定时器
    let timer = null
    // 将debounce处理结果当作函数返回
    return function () {
        // 保留调用时的this上下文
        let context = this
        // 保留调用时传入的参数
        let args = arguments
        // 每次事件被触发时,都去清除之前的旧定时器
        if(timer) { 
            clearTimeout(timer)
        }
        // 设立新定时器
        timer = setTimeout(function () {
            fn.apply(context, args)
        }, delay)
    }
}
// 用debounce来包装scroll的回调
const better_scroll = debounce(() => console.log('触发了滚动事件'), 1000) document.addEventListener('scroll', better_scroll)

小结

throttle 和 debounce 不仅是我们日常开发中的常用优质代码片段,更是前端面试中不可不知的高频考点。“看懂了代码”、“理解了过程”在本节都是不够的,重要的是把它写到自己的项目里去,亲自体验一把节流和防抖带来的性能    提升。