当前位置: 首页 > 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;}};
}
定时器法(第一次会延迟执行)
http://www.xdnf.cn/news/500113.html

相关文章:

  • C# lock
  • 端到端自动驾驶系统实战指南:从Comma.ai架构到PyTorch部署
  • 通义千问-langchain使用构建(三)
  • 2025年渗透测试面试题总结-百度面经(题目+回答)
  • YOLO (You Only Look Once)版本历程学习
  • Java泛型详解
  • Vue百日学习计划Day21-23天详细计划-Gemini版
  • Elasticsearch 官网阅读之 Term-level Queries
  • 关于软件测试开发的一些有趣的知识
  • ElasticSearch 8.x新特性面试题
  • 【论文阅读】针对BEV感知的攻击
  • python:gimp 与 blender 两个软件如何协作?
  • 5.27本日总结
  • JSP链接MySQL8.0(Eclipse+Tomcat9.0+MySQL8.0)
  • markdown 文档编辑软件 MarkText 使用教程
  • QT软件安装
  • 项目复习(1)
  • 刷leetcodehot100返航版--二叉树
  • JavaScript【7】BOM模型
  • MODBUS RTU通信协议详解与调试指南
  • 利用人工智能优化求职流程:开发一个智能求职助手
  • 【软考 程序流程图的测试方法】McCabe度量法计算环路复杂度
  • ubuntu安装google chrome
  • AtomicInteger
  • Axure制作可视化大屏动态滚动列表教程
  • 2025 年九江市第二十三届中职学校技能大赛 (网络安全)赛项竞赛样题
  • Seata源码—5.全局事务的创建与返回处理一
  • 由浮点数x的位级表示求其整型值
  • MySQL UPDATE 执行流程全解析
  • 【开源Agent框架】Suna架构设计深度解析与应用实践