当前位置: 首页 > news >正文

JavaScript防抖与节流全解析

文章目录

    • 前言:为什么需要防抖和节流
    • 基本概念与区别
      • 防抖(Debounce)
      • 节流(Throttle)
      • 关键区别
    • 防抖(Debounce)详解
      • 1. 基本防抖函数实现
      • 2. 防抖函数的使用
      • 3. 防抖函数的工作流程
      • 4. 防抖函数进阶 - 立即执行选项
    • 节流(Throttle)详解
      • 1. 基本节流函数实现
        • 时间戳法(第一次会立即执行)
        • 定时器法(第一次会延迟执行)
      • 2. 节流函数的使用
      • 3. 节流函数的工作流程
      • 4. 节流函数进阶 - 首尾调用控制
    • 实际应用场景
      • 1. 搜索框输入防抖
      • 2. 滚动加载节流
      • 3. 按钮点击防抖(防止重复提交)
      • 4. 窗口调整大小节流
    • 防抖与节流的进阶实现
      • 1. 可取消的防抖函数
      • 2. 可取消的节流函数
      • 3. 带返回值的防抖函数(使用Promise)
    • 常见问题与解决方案
      • 1. 防抖函数内无法访问this和事件对象
      • 2. React组件中使用防抖/节流
      • 3. 函数依赖项变化时重置防抖/节流
    • 第三方库的实现
      • 1. Lodash
      • 2. Underscore
      • 3. RxJS
    • 总结与最佳实践
      • 1. 选择合适的技术
      • 2. 性能考虑
      • 3. 最佳实践
      • 4. 回顾关键概念
      • 2. 性能考虑
      • 3. 最佳实践
      • 4. 回顾关键概念

前言:为什么需要防抖和节流

在前端开发中,我们经常会遇到一些高频触发的事件,例如:

  • 浏览器窗口调整大小(resize)
  • 页面滚动(scroll)
  • 鼠标移动(mousemove)
  • 键盘输入(keyup、keydown)
  • 频繁点击按钮等

如果不加控制,这些事件处理函数可能会在短时间内被频繁调用,导致以下问题:

  1. 性能问题:函数频繁执行,特别是复杂计算或DOM操作,会导致页面卡顿
  2. 资源浪费:例如输入搜索时,频繁发送不必要的API请求
  3. 不良用户体验:例如按钮重复点击导致的重复提交表单

看一个具体例子:

// 不做任何处理的搜索输入框
const searchInput = document.getElementById('search');
searchInput.addEventListener('input', function() {// 每次输入都会执行,即使用户正在快速输入console.log('执行搜索:', this.value);fetchSearchResults(this.value); // 发送请求获取搜索结果
});

在上面的例子中,当用户快速输入"JavaScript"这个词时,可能会依次触发以下请求:

  • 搜索"J"
  • 搜索"Ja"
  • 搜索"Jav"
  • 搜索"Java"
  • 搜索"JavaS"
  • 搜索"JavaScript"

显然,除了最后一个请求,前面的所有请求都是不必要的,这不仅浪费了网络资源,还可能导致服务器压力过大。

这就是为什么我们需要防抖和节流技术:它们帮助我们控制函数的执行频率,优化性能和用户体验。

基本概念与区别

防抖(Debounce)

概念:函数防抖是指在事件被触发n秒后再执行回调,如果在这n秒内事件又被触发,则重新计时。

形象比喻:电梯关门 - 当有人进入电梯后,电梯会等待一段时间再关门,如果在这段时间内又有人进入,电梯会重新计时等待,直到一段时间内没有人进入才会关门。

典型场景

  • 搜索框输入,等用户输入完毕后再发送请求
  • 窗口调整大小完成后执行重排重绘
  • 按钮提交事件防止重复提交

节流(Throttle)

概念:函数节流是指规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。

形象比喻:水龙头控制水流 - 无论你如何快速地多次拧开水龙头,水流速度都不会超过水管的限制。

典型场景

  • 滚动事件处理
  • 射击游戏中的武器发射频率限制
  • 鼠标移动事件处理

关键区别

特性防抖(Debounce)节流(Throttle)
执行时机在一段时间内没有再次触发事件后执行在一段时间内只执行一次
适用场景需要等待操作完全结束后执行需要保持一定的执行频率
执行频率不稳定,取决于事件触发频率和间隔稳定,保证一定时间内执行一次
最后一次是否执行延迟执行,一定会执行可能不会执行最后一次(取决于实现)

下面通过可视化图表来理解两者的区别:

连续事件触发:
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
│ │ │ │ │ │ │ │ │ │
0 1 2 3 4 5 6 7 8 9 (时间轴)防抖(延迟3秒):
─────────────────────→ (只在最后一次事件后等待3秒执行)↓9+3=12节流(间隔3秒):
↓       ↓       ↓     (每隔3秒执行一次)
│       │       │
0       3       6     (时间轴)

防抖(Debounce)详解

1. 基本防抖函数实现

/*** 基础版防抖函数* @param {Function} func - 需要防抖的函数* @param {number} wait - 等待时间,单位毫秒* @returns {Function} - 防抖处理后的函数*/
function debounce(func, wait) {let timeout;return function() {const context = this; // 保存this指向const args = arguments; // 保存传入的参数// 清除之前的定时器clearTimeout(timeout);// 设置新的定时器timeout = setTimeout(function() {func.apply(context, args);}, wait);};
}

2. 防抖函数的使用

// 定义一个可能频繁调用的函数
function handleSearch(searchTerm) {console.log('Searching for:', searchTerm);// 发送API请求等操作...
}// 获取输入框元素
const searchInput = document.getElementById('search-input');// 未使用防抖的版本 - 每次输入都会执行
/*
searchInput.addEventListener('input', function() {handleSearch(this.value);
});
*/// 使用防抖后的版本 - 停止输入300毫秒后才执行
const debouncedSearch = debounce(function() {handleSearch(this.value);
}, 300);searchInput.addEventListener('input', debouncedSearch);

3. 防抖函数的工作流程

以搜索框为例,当用户连续输入"hello"这个词:

时间轴: 0ms     100ms    200ms    300ms    400ms    700ms
操作:    h        e        l        l        o       (停止输入)
函数调用: 无       无       无       无       无        执行搜索"hello"

每次按键都会重置定时器,只有当用户停止输入300ms后,才会执行一次搜索。

4. 防抖函数进阶 - 立即执行选项

在某些场景下,我们可能希望第一次触发事件时立即执行函数,然后等待一段时间再允许执行下一次。例如点击提交按钮时,我们希望立即响应第一次点击,然后暂时禁用后续点击。

/*** 带立即执行选项的防抖函数* @param {Function} func - 需要防抖的函数* @param {number} wait - 等待时间,单位毫秒* @param {boolean} immediate - 是否立即执行* @returns {Function} - 防抖处理后的函数*/
function debounce(func, wait, immediate) {let timeout;return function() {const context = this;const args = arguments;const later = function() {timeout = null;if (!immediate) func.apply(context, args);};const callNow = immediate && !timeout;clearTimeout(timeout);timeout = setTimeout(later, wait);if (callNow) func.apply(context, args);};
}// 使用立即执行的防抖 - 第一次点击立即响应,后续点击被防抖
const button = document.getElementById('submit-button');
const immediateDebounceClick = debounce(function() {console.log('Button clicked!');// 提交表单等操作...
}, 1000, true);button.addEventListener('click', immediateDebounceClick);

节流(Throttle)详解

1. 基本节流函数实现

有两种常见的方式实现节流函数:时间戳法和定时器法。

时间戳法(第一次会立即执行)
/*** 时间戳实现的节流函数* @param {Function} func - 需要节流的函数* @param {number} wait - 等待时间,单位毫秒* @returns {Function} - 节流处理后的函数*/
function throttle(func, wait) {let previous = 0; // 上一次执行的时间戳return function() {const now = Date.now(); // 当前时间戳const context = this;const args = arguments;// 如果当前时间与上一次执行时间差大于等待时间if (now - previous > wait) {func.apply(context, args);previous = now;}};
}
定时器法(第一次会延迟执行)

相关文章:

  • 多平台屏幕江湖生存指南
  • 专题四:综合练习(组合问题的决策树与回溯算法)
  • 编译原理7~9
  • 数据库实验报告 数据定义操作 3
  • Oracle 高水位线(High Water Mark, HWM)
  • 【数据结构】线性表--队列
  • Echart地图数据源获取
  • [前端高频]数组转树、数组扁平化、深拷贝、JSON.stringifyJSON.parse等手撕
  • 微波至X射线波段详解2025.5.17
  • EXO 可以将 Mac M4 和 Mac Air 连接起来,并通过 Ollama 运行 DeepSeek 模型
  • 从零开始:使用 PyTorch 构建深度学习网络
  • 线性dp练习(碱基配对)
  • 计算机视觉与深度学习 | Python实现EMD-VMD-LSTM时间序列预测(完整源码和数据)
  • 系统架构设计(十):结构化编程
  • 【嵌入式DIY实例-Arduino篇】-OLED实现乒乓游戏
  • Windows 电脑中 MATLAB 安装常见问题及解决方案
  • 深入理解指针(6)
  • 【Linux网络】内网穿透
  • Java 面向对象进阶:抽象类与接口的舞蹈
  • 前端扫盲HTML
  • 出走的苏敏阿姨一路走到了戛纳,这块红毯因她而多元
  • 爬坡难下坡险,居民出行难题如何解?
  • 遭车祸罹难的村医遇“身份”难题:镇卫生院否认劳动关系,家属上诉后二审将开庭
  • 泽连斯基已离开土耳其安卡拉
  • 恒生银行回应裁员传闻:受影响的员工数目占银行核心业务员工总数约1%
  • 美F-35险被胡塞武装击中,损失增大让行动成“烂尾仗”