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

基础问答

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

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

相关推荐
程序员海军5 小时前
用了两年多 GPT,我最近开始换模型了,还折腾了个求职避坑 Agent
前端·人工智能·后端
怕浪猫6 小时前
红利窗口开着,程序员别在场边囤课
面试·github·ai编程
zttbee6 小时前
echarts图表tooltip色块失效
前端·javascript·echarts
excel7 小时前
IndexNow:让搜索引擎更快发现网站更新的主动通知协议
前端·后端·架构
Json____8 小时前
用 FastAPI + Vue3 打造一套完整的在线考试系统:从组卷到阅卷的业务闭环实践
后端·node.js·fastapi·it学习·wwwoop.com
子兮曰8 小时前
CSS 复现 23 种设计风格:从拟物到液态玻璃的成本阶梯与兜底写法【实战】
前端·css·视觉设计
虚无境8 小时前
如何用AI帮我写出实用的插件
前端·javascript·ai编程
柳杉9 小时前
从数字化到数智化:3D 可视化大屏的下一个五年
前端·ai编程·数据可视化
lll23526917799 小时前
HR如何快速整理面试沟通内容?
面试·职场和发展
零售校长9 小时前
西湖区零售门店店长面试怎么准备
面试·职场和发展·零售