JavaScript防抖与节流函数深度解析及源码实现

作者:纸飞机时间:2026-10-24 21:57:02浏览:475

本文深入解析JavaScript中防抖(debounce)与节流(throttle)的核心概念、应用场景及源码实现。通过对比电子/流体领域的起源,阐明两者在限制高频事件触发上的差异。文章详细列举了搜索框、滚动监听等典型场景,并提供了包含参数优化、取消功能及立即执行等高级特性的完整代码实现,帮助开发者彻底掌握这两个前端性能优化的关键工具。

防抖与节流函数的核心解析

理解防抖和节流的基本概念

防抖与节流的概念并非起源于软件工程,其中防抖概念源自电子元件领域,而节流则源于流体动力学中的流动现象。

  • JavaScript是一种事件驱动型语言,大量操作会触发相应事件并加入事件队列进行处理。
  • 对于某些高频触发的事件处理,若不加限制会造成显著的性能损耗,此时引入防抖和节流机制可有效限制事件的频繁触发。

目前,防抖和节流函数已是前端实际开发中至关重要的工具,同时也是面试中高频考察的知识点。

然而,许多前端开发者在面对这两个概念时感到困惑:

  • 部分开发者无法准确区分防抖与节流的区别(这也是面试常见考点);
  • 部分开发者虽能区分,却不清楚具体应用场景;
  • 部分开发者仅依赖第三方库使用,却不明其内部原理,更无法独立编写。

深入认识防抖(debounce)函数

我们可以通过图示直观理解其工作流程:

  • 当事件触发时,响应函数并不会立即执行,而是启动一个定时器等待指定时间;
  • 若在等待期间事件被密集触发,函数的执行会被不断推迟;
  • 只有当等待了一段时间且期间没有新事件触发时,响应函数才会真正执行。

防抖与节流函数的核心解析

防抖机制的应用场景十分广泛:

  • 输入框中频繁输入内容,如搜索关键词或提交表单信息;
  • 用户频繁点击按钮以触发特定事件;
  • 监听浏览器滚动事件,以执行特定的页面操作;
  • 监听浏览器窗口大小变化的resize事件。

防抖函数的实际案例演示

我们都曾遇到过这样的场景:在搜索框中输入想要查找的内容。

例如,想要搜索“MacBook”。

  • 当输入字母“m”时,为了提供更好的用户体验,系统通常会显示联想内容,这些内容存储在服务器端,因此需要发起一次网络请求;
  • 当继续输入“ma”时,系统再次发送网络请求;
  • 那么,输入完整的“macbook”一词,一共需要发送7次网络请求;
  • 这极大地消耗了系统性能,无论是前端的事件处理开销,还是对服务器造成的压力;

然而,我们需要发送这么多次网络请求吗?

  • 显然不需要,正确的做法是在合适的时机再发送网络请求;
  • 例如,如果用户快速输入“macbook”,则只需发送一次网络请求;
  • 又或者,如果用户输入“m”后停顿思考,此时“m”确实应该发送一次网络请求;
  • 也就是说,我们应该监听用户在特定时间(如500ms内)是否再次触发事件,若未再次触发,则发送网络请求;

这就是防抖的操作逻辑:只有在指定时间内没有再次触发函数时,才真正调用该函数。

深入认识节流(throttle)函数

我们可以通过图示来理解节流的过程:

  • 当事件触发时,会执行该事件的响应函数;
  • 如果事件被频繁触发,节流函数会按照固定的频率来执行响应函数;
  • 无论中间触发多少次事件,函数的执行频率始终保持固定。

防抖与节流函数的核心解析

节流函数的典型应用场景包括:

  • 监听页面的滚动事件;
  • 监听鼠标移动事件;
  • 用户频繁点击按钮的操作;
  • 游戏中的某些机制设计。

节流函数的具体应用场景分析

许多人玩过类似“飞机大战”的游戏。

在飞机大战游戏中,按下空格键会发射子弹:

  • 许多此类游戏设定,即使玩家按键频率非常快,子弹也会保持一定的发射频率;
  • 例如设定为1秒只能发射一次,即使用户在1秒内按下了10次,子弹也会保持每秒发射一颗的频率;
  • 此时事件虽然触发了10次,但响应函数只触发了一次。

防抖与节流函数的核心解析

自定义防抖和节流函数的实现思路

我们按照以下思路来实现:

防抖基本功能实现:确保具备防抖效果。

  • 优化一:优化参数传递和this指向问题。
  • 优化二:增加取消操作功能(支持cancel)。
  • 优化三:优化立即执行效果(支持第一次立即执行)。
  • 优化四:优化返回值处理。
function debounce(fn,delay,immediate=false,resultCallback){
  let timer=null
  // console.log(this)//window
  // 定义控制立即执行的变量,false表示没有执行过
  let isInvoke=false
  // 真正的处理函数
  function _debounce(...args){
    // 取消事件执行操作
    if(timer) clearTimeout(timer)
    // console.log(this)//element元素
    if(immediate&&!isInvoke){
      const result=fn.apply(this,args)
      resultCallback(result)
      isInvoke=true
    }else{
      // 延迟执行
      timer=setTimeout(()=>{
        const result=fn.apply(this,args)
        resultCallback(result)
        timer=null
        isInvoke=false
      },delay)
    }
  }
  // 封装取消请求
  _debounce.cancel=function(){
    if(timer) clearTimeout(timer)
    timer=null
    isInvoke=false
  }
  return _debounce
}

我们按照以下思路来实现节流函数:

节流函数的基本实现:确保具备节流效果。

  • 优化一:确保节流最后一次触发也能执行。
  • 优化二:增加取消功能(支持cancel)。
  • 优化三:解决返回值问题。
function throttle(fn,interval,options={leading:true,trailing:false}){
  let lastTime=0
  const {leading,trailing,resultCallback}=options
  let timer=null
  function _throttle(...args){
    const nowTime=new Date().getTime()
    // leading优化
    if(!leading&&!lastTime) lastTime=nowTime
    let remainTime=interval-(nowTime-lastTime)
    if(remainTime<=0){
      if(timer){
        clearTimeout(timer)
        timer=null
      }
      // 参数优化
      const result=fn.apply(this,args)
      if(resultCallback) resultCallback(result)
      lastTime=nowTime
      return
    } 
    // 优化trailing
    if(!timer&&trailing){
      timer=setTimeout(()=>{
        // 参数优化
        const result=fn.apply(this,args)
        if(resultCallback) resultCallback(result)
        timer=null
        lastTime=!leading?0:new Date().getTime()
      },remainTime)
    }
  }
  _throttle.cancel=function(){
    if(timer) clearTimeout(timer)
    timer = null
    lastTime = 0
  }
  return _throttle
}

总结

本文到此结束,希望能对您有所帮助,也欢迎您关注本站以获取更多相关内容!

作者最新文章