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.mzph.cn/diannao/83551.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript入门【3】面向对象

1.对象: 1.概述: 在js中除了5中基本类型之外,剩下得都是对象Object类型(引用类型),他们的顶级父类是Object;2.形式: 在js中,对象类型的格式为key-value形式,key表示属性,value表示属性的值3.创建对象的方式: 方式1:通过new关键字创建(不常用) let person new Object();// 添…

oracle主备切换参考

主备正常切换操作参考:RAC两节点->单机 (rac和单机的操作区别:就是关闭其它节点,剩一个节点操作即可) 1.主库准备 检查状态 SQL> select inst_id,database_role,OPEN_MODE from gv$database; INST_ID DATA…

端到端自动驾驶系统实战指南:从Comma.ai架构到PyTorch部署

引言:端到端自动驾驶的技术革命 在自动驾驶技术演进历程中,端到端(End-to-End)架构正引领新一轮技术革命。不同于传统分模块处理感知、规划、控制的方案,端到端系统通过深度神经网络直接建立传感器原始数据到车辆控制…

使用 Kotlin 和 Jetpack Compose 开发 Wear OS 应用的完整指南

环境配置与项目搭建 1. Gradle 依赖配置 // build.gradle (Module) android {buildFeatures {compose true}composeOptions {kotlinCompilerExtensionVersion "1.5.3"} }dependencies {def wear_compose_version "1.2.0"implementation "androidx.…

应用层协议简介:以 HTTP 和 MQTT 为例

文章目录 应用层协议简介:什么是应用层协议?为什么需要应用层协议?什么是应用层协议?为什么需要应用层协议? HTTP 协议详解HTTP 协议特点HTTP 工作的基本原理HTTP 请求与响应示例为什么 Web 应用基于 HTTP 请求&#x…

Kafka快速安装与使用

引言 这篇文章是一篇Ubuntu(Linux)环境下的Kafka安装与使用教程,通过本文,你可以非常快速搭建一个kafka的小单元进行日常开发与调测。 安装步骤 下载与解压安装 首先我们需要下载一下Kafka,这里笔者采用wget指令: wget https:…

PD 分离推理的加速大招,百度智能云网络基础设施和通信组件的优化实践

为了适应 PD 分离式推理部署架构,百度智能云从物理网络层面的「4us 端到端低时延」HPN 集群建设,到网络流量层面的设备配置和管理,再到通信组件和算子层面的优化,显著提升了上层推理服务的整体性能。 百度智能云在大规模 PD 分离…

flutter Stream 有哪两种订阅模式。

Flutter 中的 Stream 有两种订阅模式: ​单订阅模式 (Single Subscription)​​ 只能有一个订阅者(listen 只能调用一次),后续调用会抛出异常。数据仅在订阅后开始传递,适用于点对点通信场景(如文件读取流…

Python爬虫实战:研究JavaScript 环境补全逆向解密

1. 引言 1.1 研究背景与意义 随着互联网的快速发展,大量有价值的数据被发布在各种网站上。然而,为了保护数据安全和商业利益,许多网站采用了 JavaScript 加密技术对敏感数据进行保护。这些加密技术使得传统的爬虫技术难以直接获取和解析数据,给数据采集工作带来了巨大挑战…

[system-design] ByteByteGo_Note Summary

目录 通信协议 REST API 与 GraphQL gRPC 如何工作? 什么是Webhook? 如何提高应用程序接口的性能? HTTP 1.0 -> HTTP 1.1 -> HTTP 2.0 -> HTTP 3.0 (QUIC) SOAP vs REST vs GraphQL vs RPC 代码优先与应用程序接口优先 HTT…

Linux中的进程

进程控制 fork 函数 fork 函数从已存在的进程中创建新的进程,已存在进程为父进程,新创建进程为子进程 fork 的常规用法 一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程等待客户端请求,生成子…

EDR与XDR如何选择适合您的网络安全解决方案

1. 什么是EDR? 端点检测与响应(EDR) 专注于保护端点设备(如电脑、服务器、移动设备)。通过在端点安装代理软件,EDR实时监控设备活动,检测威胁并快速响应。 EDR核心功能 实时监控:…

AGI大模型(21):混合检索之混合搜索

为了执行混合搜索,我们结合了 BM25 和密集检索的结果。每种方法的分数均经过标准化和加权以获得最佳总体结果 1 代码 先编写 BM25搜索的代码,再编写密集检索的代码,最后进行混合。 from rank_bm25 import BM25Okapi from nltk.tokenize import word_tokenize import jieb…

2025最新的软件测试面试大全(含答案+文档)

一、软件测试基础面试题 1、阐述软件生命周期都有哪些阶段? 常见的软件生命周期模型有哪些? 软件生命周期是指一个计算机软件从功能确定设计,到开发成功投入使用,并在使用中不断地修改、增补和完善,直到停止该软件的使用的全过程(从酝酿到…

C++.神经网络与深度学习(二次修改)

神经网络与深度学习 1. 神经网络基础1.1 神经元模型与激活函数1.2 神经网络结构与前向传播2.1 损失函数与优化算法均方误差损失函数交叉熵损失函数梯度下降优化算法2.2 反向传播与梯度计算神经元的反向传播3.1 神经元类设计与实现神经元类代码实现代码思路3.2 神经网络类构建神…

FPGA图像处理(六)------ 图像腐蚀and图像膨胀

默认迭代次数为1,只进行一次腐蚀、膨胀 一、图像腐蚀 1.相关定义 2.图像腐蚀效果图 3.fpga实现 彩色图像灰度化,灰度图像二值化,图像缓存生成滤波模块(3*3),图像腐蚀算法 timescale 1ns / 1ps // // Des…

中国版Cursor:CodeBuddy腾讯云代码助手使用体验

我正在参加CodeBuddy「首席试玩官」内容创作大赛,本文所使用的 CodeBuddy 免费下载链接:腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴” 1.CodeBuddy简介 腾讯云代码助手CodeBuddy,这个是一款编程插件,我们可以在各个编程…

Go语言 GORM框架 使用指南

在 Go 语言社区中,数据库交互一直是开发者们关注的重点领域,不同开发者基于自身的需求和偏好,形成了两种主要的技术选型流派。一部分开发者钟情于像sqlx这类简洁的库,尽管其功能并非一应俱全,但它赋予开发者对 SQL 语句…

从零开始学习three.js(18):一文详解three.js中的着色器Shader

在WebGL和Three.js的3D图形渲染中,着色器(Shader) 是实现复杂视觉效果的核心工具。通过编写自定义的着色器代码,开发者可以直接操作GPU,实现从基础颜色渲染到动态光照、粒子效果等高级图形技术。本文将深入解析Three.j…

Python函数库调用实战:以数据分析为例

一、引言 Python之所以在编程领域广受欢迎,很大程度上得益于其丰富且强大的函数库。这些函数库涵盖了从数据分析、科学计算到Web开发、机器学习等众多领域,极大地提高了开发效率。本文将以数据分析为例,介绍如何调用Python的一些常用函数库。…