防抖节流🤔 -- 场景应用与代码手写

352 阅读6分钟

防抖函数(Debounce)

防抖函数的概念及应用场景

防抖函数是一种用于控制函数执行频率的优化技术。

在前端开发中,我们经常会遇到一些频繁触发的事件,如

  • 输入框输入
  • 按钮点击
  • 窗口大小调整
  • ...

如果不对这些事件的处理函数进行优化,可能会导致大量不必要的计算和资源消耗,甚至影响页面性能

防抖函数的作用就是确保在一定时间间隔内,函数只执行一次。

如果在这个时间间隔内再次触发函数调用,则重新计时

比如在横竖屏切换的应用中 , 就需要使用防抖函数实现立即刷新适配 倔友可以移步我的这篇文章 ,一探究竟 ~

探索CSS响应式设计:从原理到实战大家好 ,我是一名在校大二学生 , 今天,我们探索一下CSS响应式设计中的一些核心要点 - 掘金 (juejin.cn))

防抖函数的实现思路

  • 使用一个变量(通常为timeoutId)来存储定时器的 ID。
  • 在每次函数调用时,清除之前的定时器(如果存在),以防止上一次的定时器执行函数。
  • 设置一个新的定时器,当定时器到期时,执行目标函数。

基础防抖函数实现代码示例

function debounce(func, delay) {
    let timeoutId;

    return function (...args) {
        // 清除之前的定时器
        if (timeoutId) {
            clearTimeout(timeoutId);
        }

        // 设置一个新的定时器
        timeoutId = setTimeout(() => {
            func.apply(this, args);
        }, delay);
    };
}

// 测试代码
function onInput() {
    console.log('Input event triggered:', Date.now());
}

const debouncedOnInput = debounce(onInput, 1000);

// 模拟输入事件
setInterval(debouncedOnInput, 200); // 每200毫秒触发一次输入事件

在上述代码中,debounce函数接受一个函数func和一个延迟时间delay作为参数。

它返回一个新的函数,当新函数被调用时,会先清除之前的定时器(如果有),然后设置一个新的定时器,在延迟delay毫秒后执行func函数。

这样,在频繁调用新函数时,只有在最后一次调用后的delay毫秒后,func函数才会真正执行。

立即执行选项的添加

有时我们希望在首次调用防抖函数时立即执行函数,而不是等待延迟时间。

为此,我们可以为防抖函数添加一个immediate选项,用于控制函数是否在首次调用时立即执行。

function debounce(func, delay, immediate = false) {
    let timeoutId;

    return function (...args) {
        const callNow = immediate &&!timeoutId;

        // 清除之前的定时器
        if (timeoutId) {
            clearTimeout(timeoutId);
        }

        // 设置一个新的定时器
        timeoutId = setTimeout(() => {
            timeoutId = null;
            if (!immediate) {
                func.apply(this, args);
            }
        }, delay);

        if (callNow) {
            func.apply(this, args);
        }
    };
}

// 测试代码
function onInput() {
    console.log('Input event triggered:', Date.now());
}

const debouncedOnInput = debounce(onInput, 1000, true);

// 模拟输入事件
setInterval(debouncedOnInput, 200); // 每200毫秒触发一次输入事件

在这个扩展后的debounce函数中,当immediatetruetimeoutId不存在时(即首次调用),会立即执行函数。

然后再设置定时器,在延迟时间后,如果immediatefalse,则再次执行函数。

防抖函数的实际应用场景

输入框输入事件:在用户输入时,防抖函数可以限制输入事件处理函数的执行频率,从而减少不必要的请求或计算。

例如,实时搜索功能中,当用户输入关键字时,我们可以使用防抖函数来延迟发送搜索请求,直到用户停止输入一段时间后再执行搜索操作,避免频繁请求服务器。

按钮点击事件:在用户频繁点击按钮时,防抖函数可以限制点击事件处理函数的执行频率,从而避免重复操作。

比如提交表单按钮,防止用户多次快速点击导致重复提交数据。

窗口调整大小事件:在用户调整浏览器窗口大小时,防抖函数可以限制调整大小事件处理函数的执行频率,从而避免频繁的布局重绘。

因为窗口大小调整可能会触发大量的计算和重绘操作,使用防抖函数可以优化性能。

节流函数(Throttle)

节流函数的概念及应用场景

节流函数同样用于限制函数的执行频率,但它确保在一段时间内,无论触发多少次事件,函数都只执行一次。

在前端开发中,对于一些高频率触发的事件,如onresizescrollmousemove等,如果不加以限制,可能会导致浏览器性能问题。

节流函数通过限制函数的执行频率,有效解决了这个问题。它适用于以下场景:

页面滚动和大小调整

当页面滚动或窗口大小调整时,可能需要执行某些操作,如懒加载数据或调整布局。通过节流函数,可以确保这些操作不会过于频繁地执行。

高频率的事件处理

如按钮被高频点击、游戏中的技能冷却等,通过节流函数可以限制这些操作的执行频率。

DOM操作和资源加载

在高频事件触发时进行DOM操作或资源加载,通过节流函数可以减少不必要的计算和资源消耗。

节流函数的实现方式

节流函数常见的实现方式有时间戳方案和定时器方案。

时间戳方案实现代码示例

function throttle(fn, wait) {
    let lastTime = 0;
    return function () {
        const now = new Date().getTime();
        if (now - lastTime > wait) {
            fn.apply(this, arguments);
            lastTime = now;
        }
    };
}

在这个时间戳方案的节流函数中,throttle函数接受一个要节流的函数fn和一个等待时间wait(单位毫秒)。

它返回一个新的函数,在每次调用新函数时,会获取当前时间now,然后与上次执行时间lastTime进行比较。如果时间差大于等待时间wait,则执行原始函数fn,并更新lastTime为当前时间。否则,忽略此次调用。

定时器方案实现代码示例

function throttle(fn, wait) {
    let timer = null;
    return function () {
        if (!timer) {
            fn.apply(this, arguments);
            timer = setTimeout(() => {
                timer = null;
            }, wait);
        }
    };
}

在定时器方案的节流函数中,throttle函数同样接受fnwait参数。当新函数被调用时,如果定时器timer不存在(即当前没有正在等待执行的定时器),则立即执行fn函数,并设置一个定时器,在wait毫秒后清除定时器(即将timer设置为null)。

这样,在定时器未到期前,再次调用新函数时,由于timer不为null,函数将不会被执行,从而实现了节流的效果。

节流函数与防抖函数的对比

防抖函数和节流函数都用于控制函数的执行频率,但它们的触发时机有所不同。

防抖函数在事件触发后等待一段时间,如果在这段时间内没有再次触发事件,则执行函数;如果再次触发事件,则重新计时。

节流函数在规定的时间间隔内,无论事件触发多少次,函数只会执行一次。

例如在页面滚动事件中:

如果使用防抖函数,只有在用户停止滚动一段时间后才会执行相应操作;

如果使用节流函数,则会按照固定的时间间隔执行操作,无论用户是否在持续滚动。

在实际应用中,需要根据具体需求选择合适的函数来优化性能。