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

JS 节流(Throttle)与防抖(Debounce)详解

JS 节流(Throttle)与防抖(Debounce)详解

文章目录

  • JS 节流(Throttle)与防抖(Debounce)详解
    • 一、核心概念对比
    • 二、防抖(Debounce)实现与应用
      • 1. 基础实现
      • 2. 立即执行版本
      • 3. 典型应用场景
    • 三、节流(Throttle)实现与应用
      • 1. 时间戳版(立即执行)
      • 2. 定时器版(延迟执行)
      • 3. 综合版(首尾都执行)
      • 4. 典型应用场景
    • 四、可视化执行过程
      • 防抖执行流程(延迟500ms)
      • 节流执行流程(间隔500ms)
    • 五、Lodash 最佳实践
      • 1. 防抖使用
      • 2. 节流使用
    • 六、性能优化建议
    • 七、常见问题解答
      • Q1:防抖和节流如何选择?
      • Q2:为什么我的防抖函数没有立即执行?
      • Q3:移动端和PC端的延迟设置是否应该不同?
    • 八、手写实现测试题
      • 实现一个带取消功能的防抖函数

一、核心概念对比

特性防抖(Debounce)节流(Throttle)
定义连续触发事件时,只在最后执行一次连续触发事件时,固定间隔执行一次
执行时机停止触发后的延迟时间到达按照固定时间间隔执行
类比电梯关门(最后一个人进入后关门)水龙头滴水(固定频率滴水)
典型场景搜索框输入、窗口resize滚动事件、高频点击

二、防抖(Debounce)实现与应用

1. 基础实现

function debounce(fn, delay) {let timer = null;return function(...args) {clearTimeout(timer); // 清除之前的定时器timer = setTimeout(() => {fn.apply(this, args);}, delay);};
}

2. 立即执行版本

function debounceImmediate(fn, delay, immediate = true) {let timer = null;return function(...args) {if (timer) clearTimeout(timer);if (immediate && !timer) {fn.apply(this, args);}timer = setTimeout(() => {timer = null;if (!immediate) {fn.apply(this, args);}}, delay);};
}

3. 典型应用场景

// 搜索框输入
const searchInput = document.getElementById('search');
searchInput.addEventListener('input', debounce(() => {console.log('发送搜索请求:', searchInput.value);
}, 500));// 窗口resize
window.addEventListener('resize', debounce(() => {console.log('窗口大小调整完成');
}, 200));

三、节流(Throttle)实现与应用

1. 时间戳版(立即执行)

function throttle(fn, delay) {let lastTime = 0;return function(...args) {const now = Date.now();if (now - lastTime >= delay) {fn.apply(this, args);lastTime = now;}};
}

2. 定时器版(延迟执行)

function throttleTimer(fn, delay) {let timer = null;return function(...args) {if (!timer) {timer = setTimeout(() => {fn.apply(this, args);timer = null;}, delay);}};
}

3. 综合版(首尾都执行)

function throttleFull(fn, delay) {let timer = null;let lastTime = 0;return function(...args) {const now = Date.now();const remaining = delay - (now - lastTime);if (remaining <= 0) {if (timer) {clearTimeout(timer);timer = null;}fn.apply(this, args);lastTime = now;} else if (!timer) {timer = setTimeout(() => {fn.apply(this, args);timer = null;lastTime = Date.now();}, remaining);}};
}

4. 典型应用场景

// 滚动事件
window.addEventListener('scroll', throttle(() => {console.log('处理滚动逻辑');
}, 200));// 高频按钮点击
const btn = document.getElementById('submit');
btn.addEventListener('click', throttle(() => {console.log('提交表单');
}, 1000));

四、可视化执行过程

防抖执行流程(延迟500ms)

事件触发: |-----|-----|-----|-----|-----|
函数执行:         |_________|(最后一次触发后500ms执行)

节流执行流程(间隔500ms)

事件触发: |-----|-----|-----|-----|-----|
函数执行: |_____|_____|_____|(固定每500ms执行一次)

五、Lodash 最佳实践

1. 防抖使用

import { debounce } from 'lodash';// 基本用法
const handleSearch = debounce(searchAPI, 300);// 带选项配置
const saveEditor = debounce(autoSave, 1000, {leading: true,  // 首次立即执行trailing: false // 不执行尾部调用
});

2. 节流使用

import { throttle } from 'lodash';// 基本用法
const handleScroll = throttle(updatePosition, 200);// 带选项配置
const recordTime = throttle(savePlayTime, 1000, {leading: false,  // 首次不立即执行trailing: true   // 执行尾部调用
});

六、性能优化建议

  1. 合理设置延迟时间

    • 防抖:输入类建议300-500ms,resize建议100-200ms
    • 节流:滚动建议100-200ms,动画建议16.7ms(60fps)
  2. 内存管理

    // 组件销毁时取消防抖/节流
    const debouncedFn = debounce(fn, 300);
    window.addEventListener('resize', debouncedFn);// 组件卸载时
    window.removeEventListener('resize', debouncedFn);
    debouncedFn.cancel(); // Lodash提供的取消方法
    
  3. RAF节流(适合动画场景):

    function throttleRAF(fn) {// 标记变量,用于判断是否已经有动画帧回调在等待执行let ticking = false;/*** 节流处理后的函数* @param {...any} args - 传递给原始函数的参数*/return function(...args) {// 如果没有正在等待执行的动画帧回调if (!ticking) {// 请求浏览器在下一次重绘前执行回调requestAnimationFrame(() => {// 执行原始函数,并保持正确的 this 上下文和参数fn.apply(this, args);// 执行完成后重置标记ticking = false;});// 设置标记,表示已经有动画帧回调在等待ticking = true;}};
    }
    

七、常见问题解答

Q1:防抖和节流如何选择?

  • 需要最终状态用防抖(如搜索建议)
  • 需要过程状态用节流(如滚动加载)

Q2:为什么我的防抖函数没有立即执行?

检查是否设置了leading: false(Lodash)或未实现立即执行逻辑

Q3:移动端和PC端的延迟设置是否应该不同?

是的,移动端建议适当增加延迟(如PC用300ms,移动端用500ms)

八、手写实现测试题

实现一个带取消功能的防抖函数

function cancellableDebounce(fn, delay) {// 存储定时器ID的变量,初始为nulllet timer = null;/*** 防抖处理后的函数* @param {...any} args - 传递给原始函数的参数*/const debounced = function(...args) {// 每次调用先清除之前的定时器clearTimeout(timer);// 设置新的定时器timer = setTimeout(() => {// 延迟结束后,使用正确this和参数执行原始函数fn.apply(this, args);}, delay);};/*** 取消当前等待执行的防抖函数*/debounced.cancel = function() {// 清除定时器clearTimeout(timer);// 重置定时器变量timer = null;};// 返回包装后的防抖函数return debounced;
}{// 延迟结束后,使用正确this和参数执行原始函数fn.apply(this, args);}, delay);};/*** 取消当前等待执行的防抖函数*/debounced.cancel = function() {// 清除定时器clearTimeout(timer);// 重置定时器变量timer = null;};// 返回包装后的防抖函数return debounced;
}
http://www.lqws.cn/news/159121.html

相关文章:

  • MCP实践
  • MySQL 的锁机制【深度全面】
  • HBuilder 发行Android(apk包)全流程指南
  • Flyway
  • Spring WebFlux 整合AI大模型实现流式输出
  • 数据库优化实战分享:高频场景下的性能调优技巧与案例解析
  • c#基础010(程序结构)
  • 深度解析数字营销专属大模型 AdLLM 的训练思路
  • 监控硬盘可以当台式机硬盘用吗
  • 【数据结构】5. 双向链表
  • Vue3解决“找不到模块@/components/xxx.vue或其相应的类型声明ts文件(2307)”
  • BLOB 是用来存“二进制大文件”的字段类型
  • GO协程(Goroutine)问题总结(待续)
  • 自建 Derp 中继节点
  • [蓝桥杯]航班时间
  • RK3588 InsightFace人脸识别移植及精度测试全解析
  • UE Learning Record
  • 在嵌入式中C语言中static修饰的变量常量和字符串常量存储位置
  • EFI(x64)简易开发环境
  • 优化Docker容器化安装与配置的最佳实践
  • 将图形可视化工具的 Python 脚本打包为 Windows 应用程序
  • Java线程安全集合类
  • 贪心,回溯,动态规划
  • HTV 3.3 | 秒播无卡顿 直播源每天维护更新
  • 【定昌linux开发板】关闭ssh 端口 22
  • Rocketmq消息队列 消息模型 详解
  • 虚拟机网络配置
  • css实现文字颜色渐变
  • 深入理解汇编语言子程序设计与系统调用
  • 第十三节:第四部分:集合框架:HashMap、LinkedHashMap、TreeMap