一文教你彻底学会JavaScript手写防抖节流

作者:异步漫步

时间:2026-10-26

浏览:367

本文详细解析JavaScript中防抖与节流的核心概念及手写实现。通过生活实例类比,深入讲解防抖(debounce)与节流(throttle)的区别,并提供包含闭包、定时器、this指向及参数处理的完整代码实现,帮助开发者掌握前端性能优化的关键技巧。

文章目录

前言

  • 近期有开发者反馈,在面试中常因混淆防抖与节流的概念而遇到困难。
  • 防抖和节流不仅是面试中的常见考点,在实际项目开发中更是提升性能的关键技术,因此掌握它们至关重要。
  • 接下来,我们将通过简短的时间,深入解析并彻底掌握防抖与节流的手写实现。

防抖

实例说明

抛开抽象概念,生活中防抖的例子随处可见:

  • 例如电脑在不使用一段时间后会自动进入休眠状态。
  • 当你再次操作电脑时,系统会被重新激活,并开始一个新的10分钟倒计时。
  • 在这10分钟内,如果你继续操作电脑,倒计时将重置为10分钟。
  • 只有当你最后一次操作后,经过完整的10分钟没有任何操作,电脑才会真正进入休眠。

前言

这就是防抖的核心概念:在一段连续的操作时间内,无论操作多少次,最终只执行一次。

上述例子中的电脑操作可理解为触发事件,而setTimeout和clearTimeout则是实现防抖的关键要素。

在实际开发中,防抖应用广泛,例如调整窗口大小时的重绘优化、表单提交时的请求频率控制,以及防止按钮重复点击等场景。

防抖函数实现

理解了概念后,我们来动手实现一个防抖函数。假设有一个按钮,点击后需要调用接口新增数据,我们希望多次快速点击只触发一次新增操作,该如何实现?

前言

首先,我们模拟一个按钮点击的场景。

// debounce.js
let addBtn=document.getElementById('add')
function addOne(){
  console.log('增加一个')
}
addBtn.addEventListener('click',addOne)

为了处理事件,我们需要封装addOne函数。

// debounce.js
let addBtn=document.getElementById('add')
function addOne(){
  console.log('增加一个')
}
function debounce(fun){
  return function(){
    fun()
  }
}
addBtn.addEventListener('click',debounce(addOne))

这里利用闭包处理addOne函数,并引入延时器setTimeout进行倒计时,点击后延迟2s再执行。

// debounce.js
function debounce(fun,time){
  return function(){
    setTimeout(()=>{
      fun()
    },time)
  }
}
addBtn.addEventListener('click',debounce(addOne,2000))

虽然达到了延时效果,但每次点击都会创建新的setTimeout,无法实现“多次点击只执行一次”的目标。

此时,clearTimeout发挥作用。在点击按钮触发debounce时,我们需要先清除之前的setTimeout,然后重新计时。

function debounce(fun,time){
  let timer
  return function(){
    clearTimeout(timer)
    timer=setTimeout(()=>{
      fun()
    },time)
  }
}

至此,基本的防抖功能已完成。但如果在addOne()中打印this,会发现this指向的是Window,这通常不是我们期望的结果。

我们需要使用apply来修正this的指向,同时考虑执行函数的参数传递,通过arguments处理不同函数的参数。

function debounce(fun,time){
  let timer
  return function(){
    clearTimeout(timer)
    let args = arguments
    timer=setTimeout(()=>{
      fun.apply(this,args)
    },time)
  }
}

这样,一个完整的防抖函数就手写完成了,实现过程并不复杂。

总结来说,防抖是指在频繁的操作(如输入、点击)中,最终只执行一次,以此提升性能。

节流

实例说明

生活中节流的例子同样常见,例如公园的自动洒水机:

  • 通常我们会设定一个时间间隔,假设30min。
  • 当距离上次洒水的时间不足30min时,洒水机保持静止。
  • 只有当时间达到30min时,才会触发洒水动作。

前言

  • 这就是节流的基本概念:在固定的时间间隔内执行一次操作。
  • 上述例子中,洒水代表触发事件,而30min和当前时间是节流的主要判断要素。
  • 在开发中,节流常用于鼠标滚轮监听滚动位置、防止按钮重复点击等场景。

节流函数实现

理解了节流概念后,我们来实现它。假设要实现一个鼠标滚动打印事件,要求每3秒执行一次,该如何编写?

前言

首先模拟一个触发事件。

// throttle.js
function scrollTest(){
  console.log('现在我触发了')
}
document.addEventListener('scroll',scrollTest)

接下来封装节流函数,与防抖类似,利用闭包并增加参数接收节流时间。

// throttle.js
...
function throttle(fun,time){
  return function(){
    fun()
  }
}
document.addEventListener('scroll',throttle(scrollTest,3000))

节流的核心是“一段时间内只执行一次”,如果两次操作的时间间隔未达到设定值,则不执行。

我们可以通过记录每次操作的时间戳来进行对比。

// throttle.js
...
function throttle(fun,time){
  let t1=0 //初始时间
  return function(){
    let t2=new Date() //当前时间
    if(t2-t1>time){
      fun()
      t1=t2
    }
  }
}

我们需要记录两个时间:t1代表初始时间,t2代表当前时间。如果当前时间距离初始时间大于设定的time,

则执行fun(),并将初始时间更新为当前执行的时间。这样,每次执行后t1就变为了上一次执行的时间。

至此,基本的节流功能已完成。

// throttle.js
...
function throttle(fun,time){
  let t1=0 //初始时间
  return function(){
    let t2=new Date() //当前时间
    if(t2-t1>time){
      fun()
      t1=t2
    }
  }
}

与防抖一样,我们也需要处理this的指向和参数接收,最终的实现代码如下。

// throttle.js
...
function throttle(fun,time){
  let t1=0 //初始时间
  return function(){
    let t2=new Date() //当前时间
    if(t2-t1>time){
      fun.apply(this,arguments)
      t1=t2
    }
  }
}

这样,一个节流函数就手写完成了,同样并不困难。

总结来说,节流是指在一段时间内频繁操作,但在规定的时间内只执行一次,以此提升性能。

总结

  • 感谢阅读至此,本文到此结束。
  • 区分防抖与节流的一个直观方法是:在10秒内疯狂点击按钮,若使用防抖,则只执行一次;若使用节流,则会每隔一段时间执行一次,具体时间由你控制。
  • 防抖和节流还有多种变形,可根据不同需求调整函数实现,但核心原理不变。掌握上述方法,即可应对各种场景。

以上是关于JavaScript手写防抖节流的详细内容,更多相关技术资料请查阅本站其他文章。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章