『 每日一题』手写每日一题之防抖

1,522 阅读3分钟

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

在面试中,我们经常会遇到需要手写代码的面试题,为了让我们的面试能够准备的更加充分,我们有必要好好的准备一下相关的手写题,正好就开了这个每日一题的专栏,旨在帮助大家熟悉场景的手写题。

今天我们就来学习面试中常见的一道手写题 -- 函数防抖

在写这个函数防抖之前,我们需要先了解它的定义,然后再一步一步分析该如何实现,而不是一上来就直接去死记硬背代码,这样就算你把代码都背下来了,但是在实际的开发中你还是不知道具体该如何使用。

防抖函数 debounce 指的是某个函数在某段时间内,无论触发了多少次回调,都只执行最后一次。

实现原理就是利用定时器,函数第一次执行时设定一个定时器,之后调用时发现已经设定过定时器就清空之前的定时器,并重新设定一个新的定时器,如果存在没有被清空的定时器,当定时器计时结束后触发函数执行。

有了上述关于函数防抖的描述,接下来我们就可以开始写代码了。首先我们定义一个 debounce 函数,这个命名也是约定俗成的,在这个函数中,我们会用到定时器 setTimeout,然后再后续每次触发时,都需要先判断一下这个定时器是否已经存在了,如果存在则将定时器清空,我们来看代码,如下:

/*
 * fn 要执行的函数
 * wait 延时执行的时间,默认为300毫秒
 */
const debounce = (fn, wait = 300) => {
    // 首先定义一个变量,用于存储定时器
    let timer;
    // 返回一个执行的函数
    return function (...args) {
        // 保存当前函数的this指向
        const _self = this;
        // 如果定时器已经存在了,则需要清除
        if (timer) clearTimeout(timer);
        // 重新执行当前的函数
        timer = setTimeout(() => {
            fn.apply(_self, args);
        }, wait);
    }
};

在上述代码中,我们根据前面的描述信息,一步步实现了这个函数防抖,下面我们来看一下在实际开发中是如何使用的,相关代码如下:

// 使用防抖函数包裹一个新的函数
const debounceFn = debounce(() => console.log('fn 防抖执行了'), 500);
// 绑定一个滚动事件
document.addEventListener('scroll', debounceFn);

在实际的开发中,当我们给 document 绑定一个 scroll 事件时,如果不添加函数防抖,则当我们在页面中执行滚动操作时,会持续的执行;而当我们加了一个函数防抖的效果后,则只有当我们停止滚动后,才会执行最后一次,这样做的好处是可以降低浏览器的频繁刷新。

接下来我们可以看一下业界比较有名的库 loadsh 中是如何实现 debounce 函数的,这里截取部分源码,如下所示:

function debounce(func, wait, options) {
    ...other code
    
    function invokeFunc(time) {
        const args = lastArgs;
        const thisArg = lastThis;

        lastArgs = lastThis = undefined;
        lastInvokeTime = time;
        result = func.apply(thisArg, args);
        return result;
    }

    function startTimer(pendingFunc, wait) {
        if (useRAF) {
            root.cancelAnimationFrame(timerId);
            return root.requestAnimationFrame(pendingFunc);
        }
        return setTimeout(pendingFunc, wait);
    }

    function cancelTimer(id) {
        if (useRAF) {
            return root.cancelAnimationFrame(id);
        }
        clearTimeout(id);
    }
    
    ...other code
}

loadshdebounce 源码中,我们也能看到它的实现中使用了 setTimeout,而我们在面试中能够实现上面的那个方法即可,当然在实际的开发中,我们自己手写的 debounce 函数并不完善,因此我们也可以借助像 loadsh 这样比较有名的第三方库。

最后,大家要明白一点,面试是面试,工作是工作。