【每日一面】手写防抖函数

基础问答

问:手写一个防抖函数 答:

typescript 复制代码
/**
 * 基础版防抖函数(非立即执行)
 * @param {Function} func - 需要防抖的目标函数
 * @param {number} delay - 等待时间(毫秒)
 * @returns {Function} - 防抖后的函数
 */
function debounce(func, delay) {
  let timer = null; // 用闭包存储定时器ID,确保多次调用共享同一个定时器

  // 返回防抖后的函数,接收目标函数的参数(...args)
  return function (...args) {
    // 1. 若已有定时器,先清除(重复触发时重新计时)
    if (timer) clearTimeout(timer);

    // 2. 重新设置定时器,等待delay后执行目标函数
    timer = setTimeout(() => {
      func.apply(this, args); // 用apply绑定this(确保目标函数this指向正确)
      timer = null; // 执行后清空定时器,避免内存泄漏
    }, delay);
  };
}

扩展延伸

防抖(Debounce)

  • 核心逻辑:当一个事件会被频繁的触发时,防抖函数不会频繁的执行,而是等待事件停止触发一段时间后才执行;如果在等待执行的过程中,事件再次被触发,则我们需要重新计算需要等待的时间。
  • 典型使用场景:搜索框输入联想(每次按键都会触发输入对应 Input 的事件,我们认为用户结束输入后,我们再进行联想,这个结束输入的判定规则就是,自上一次Input事件触发后的一段时间内,用户没有再触发Input事件,可视为结束输入,需要开始联想),实时输入校验等。

和防抖并列提及的是节流。

节流(Throttle)

  • 核心逻辑:当事件会被频繁触发的时候,节流函数只会按照固定的时间间隔执行,无论期间事件被处罚多少次,都只在每个时间的开头(或结束)执行一次。
  • 典型使用场景:滚动监听(一般用于加载数据判断,每隔一段时间判断一次当前位置,来判断需要加载的数据量),窗口resize事件(用于重新计算布局)

这里简单对比一下防抖和节流:

防抖 节流
执行时机 事件停止触发后,等待一段指定的时间 固定时间间隔执行,每个时间段内仅执行一次
重复触发的问题 重新计算等待的时间,执行会延迟 不影响,固定时间间隔执行
目标 解决冗余的执行 解决过度的执行
使用场景 搜索、校验 滚动加载

搜索输入的过程中,每次键入字符触发搜索,在没有防抖的情况下,仅仅只有最后一次的搜索(即用户输入完成)才是有效的,之前的这些,全部都是没有意义的,只会加重服务负担,即为 "冗余"

页面滚动过程中,滚动是持续触发的,在没有节流的情况下,每一次滚动都会有大量的计算过程(假设你的滚动事件是有计算操作的),计算阻塞主线程,会导致页面卡顿,无法正常滚动,即为 "过度" ,如果使用防抖,滚动事件的持续触发,会导致计算一直无法开始,俗称"不跟手"。

面试追问

  1. setTimeout 的延时并不准,有没有办法实现一个更精确的时间检测? 有,使用时间戳 + requestFrameAnimation 实现。

  2. 页面滚动加载数据一般用什么?搜索框输入触发联想词,又用什么? 滚动加载一般用节流,防抖需要等用户停止滚动才加载,可能会等很久,节流则是一到底部就加载,可以保证加载的及时性。 搜索联想一般用防抖,因为用户的输入过程会频繁触发联想,但是只有用户停止输入时,触发的联想才是用户想要的、有效的。

  3. 我看你在防抖函数中,用了 apply 这是为啥?为啥不可以直接用 func ? 主要是 this 指针的指向问题,防抖函数返回的是一个新的函数,假设现在设置的是 input.oninput = debounceSearch,这个 debounceSearch 中如果有 this,那么预期是要指向 input 标签,但是我们直接调用 func 的话,this 会指向 window 或 undefined,和预期不一致。

  4. 防抖函数中,如果目标函数有返回值,我们可以拿到吗? 不行,即使返回目标函数结果也不行,因为他在 setTimeout 里面执行的,无法返回对应的执行结果。

  5. 但是我就需要这个返回结果,有没有办法? 有,两种办法,一是将 setTimout 用 Promise 封装起来,setTimeout 的回调执行时,resolve 这个 Promise 就可以了,这样防抖函数就变成了一个异步的api,二是使用回调参数,在目标函数执行后,调用这个回调就可以了。

  6. 有没有遇到过防抖函数导致内存泄漏的情况? 没有,但是防抖函数有内存泄漏的可能性,本质上是闭包写法产生的,编写代码的时候注意闭包的处理就可以了。

相关推荐
FungLeo3 小时前
成为全栈·Node 后端篇·全文搜索:从 LIKE 到全文索引
node.js·全文索引·模糊搜索·全文搜索·成为全栈·like 搜索
再吃一根胡萝卜4 小时前
08 · 前端:Vue3 + SSE 流式与执行链路可视化
面试
再吃一根胡萝卜4 小时前
03 · 后端:FastAPI 与分层架构
面试
mldong5 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排6 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60616 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角7 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!7 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰8 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
Interview Aid1128 小时前
Walmart Global Tech SDE 三轮面经|基础、并发、压力面
面试·职场和发展