javascript高级 --- 函数防抖和函数节流

本文探讨了在网页开发中,如何通过函数防抖(debounce)和函数节流(throttle)来优化性能,减少因频繁DOM操作引起的重排与重绘问题。详细介绍了两种方法的实现原理及应用场景。

一、介绍
在某些特定场景下,函数可能会被频繁的调用,回调中又会发生DOM操作,浏览器继而重排与重绘,造成很严重的性能问题。

二、案例
例如:window.onresize / window.onscroll、频繁的onmousemove onkeyup

解决类似问题:函数防抖(debounce)、函数节流(throttle),核心就是限制一个函数的频繁触发


函数防抖:

顾名思义,利用函数实现防抖动,触发函数时,元素的位置或者尺寸发生变化,导致页面回流,进而元素抖动,通过函数防抖,使得函数在一定时间内延迟执行一次,从而减少页面回流

通过定时器延迟执行当前函数

function debounce(fun, time = 800){
	clearTimeout(fun.timer)
	fun.timer = setTimeout(function = > {
		fun()
	},time)
}

debounce(scroll())

函数防抖基本实现,会发现当前的event对象为undefined,强制绑定this,并且赋值参数

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

debounce(scroll())

函数节流:

限制函数执行的频率,如果当前函数未执行完毕,未来的执行都会被忽略

function throttle(fun, time = 800) {
	if (fun.timer) return
	fun.timer = setTimeout(function = > {
		fun.apply(this,arguments)
		clearTimeout(fun.timer)
	},time)
}

debounce(scroll())
<think>我们正在讨论如何实现JavaScript中的节流(throttle)防抖(debounce)来控制回调函数的执行频率,以解决多次回调后停止响应的问题。根据提供的引用,我们可以总结出以下实现方法: ### 1. 节流(Throttle) 节流的原理:在一定时间间隔内,无论事件触发多少次,只执行一次回调函数。这样可以保证在密集的事件触发中,按照固定的时间间隔执行回调。 #### 实现方法(基于引用[1]引用[3]): ```javascript function throttle(fn, delay) { let timerId = null; return function(...args) { if (!timerId) { fn.apply(this, args); timerId = setTimeout(() => { timerId = null; }, delay); } }; } ``` **使用示例**: ```javascript // 滚动事件节流 window.addEventListener('scroll', throttle(function(e) { console.log('Scroll event throttled'); }, 200)); ``` ### 2. 防抖(Debounce) 防抖的原理:事件触发后,等待一段时间(延迟)再执行回调。如果在这段等待时间内事件再次被触发,则重新开始计时。也就是说,只有事件停止触发一段时间后,回调才会执行。 #### 实现方法(基于引用[3]): ```javascript function debounce(fn, delay) { let timerId = null; return function(...args) { clearTimeout(timerId); timerId = setTimeout(() => { fn.apply(this, args); }, delay); }; } ``` **使用示例**: ```javascript // 输入框防抖 inputElement.addEventListener('input', debounce(function(e) { console.log('Input value:', e.target.value); }, 300)); ``` ### 在框架中的使用 #### Vue(引用[2]引用[3]) 在Vue中,我们可以将节流防抖函数直接包裹在方法中: ```javascript import { throttle, debounce } from '@/utils/utils.js'; // 或者使用lodash export default { methods: { // 节流处理滚动事件 handleScroll: throttle(function(e) { // 处理逻辑 }, 200), // 防抖处理输入 handleInput: debounce(function(e) { // 处理逻辑 }, 300) } } ``` #### React(引用[4]) 在React中,我们可以使用`useRef``useCallback`来实现节流防抖,并注意在组件卸载时清理定时器: ```javascript import { useRef, useCallback, useEffect } from 'react'; function useDebounce(fn, delay) { const timerRef = useRef(null); useEffect(() => { // 组件卸载时清理定时器 return () => { if (timerRef.current) { clearTimeout(timerRef.current); } }; }, []); return useCallback((...args) => { if (timerRef.current) { clearTimeout(timerRef.current); } timerRef.current = setTimeout(() => { fn(...args); }, delay); }, [fn, delay]); } // 使用示例 function MyComponent() { const handleChange = useDebounce((value) => { console.log(value); }, 300); return <input onChange={(e) => handleChange(e.target.value)} />; } ``` ### 总结 - **节流**:适用于需要在一定时间内至少执行一次回调的场景,如滚动事件、窗口调整等。 - **防抖**:适用于事件结束后再执行回调的场景,如搜索框输入、表单验证等。 通过合理使用节流防抖,可以避免回调函数被过于频繁地调用,从而解决多次回调后停止响应的问题,同时提升页面性能。 §§相关问题§§ 1. 节流防抖在性能优化中的应用场景有哪些区别? 2. 如何在Vue3的Composition API中实现节流防抖? 3. 节流防抖函数的时间参数应该如何选择? 4. 除了节流防抖,还有哪些方法可以优化高频率事件的性能?</think>### JavaScript 实现回调函数节流(Throttle)防抖(Debounce)的方法 #### 1. 节流(Throttle)实现 节流的核心思想是**在指定时间间隔内只执行一次回调**,适用于需要均匀执行回调的场景(如滚动事件): ```javascript function throttle(fn, delay) { let timerId = null; return function(...args) { if (!timerId) { fn.apply(this, args); // 立即执行回调 timerId = setTimeout(() => { timerId = null; // 重置状态 }, delay); } }; } // 使用示例 const throttledScroll = throttle((position) => { console.log('Scroll position:', position); }, 200); window.addEventListener('scroll', () => { throttledScroll(window.scrollY); }); ``` **原理说明**: 1. 首次调用立即执行回调 2. 设置定时器,在延迟期间阻止后续调用 3. 延迟结束后重置状态,允许下一次调用 4. 确保每200ms最多执行一次回调 #### 2. 防抖(Debounce)实现 防抖的核心思想是**事件停止触发后才执行回调**,适用于需要"最终状态"的场景(如搜索框输入): ```javascript function debounce(fn, delay) { let timerId = null; return function(...args) { clearTimeout(timerId); // 清除前一个定时器 timerId = setTimeout(() => { fn.apply(this, args); // 延迟后执行回调 }, delay); }; } // 使用示例 const debouncedSearch = debounce((query) => { console.log('Searching for:', query); }, 300); searchInput.addEventListener('input', (e) => { debouncedSearch(e.target.value); }); ``` **原理说明**: 1. 每次调用都重置定时器 2. 只有最后一次调用后的延迟时间内没有新调用才会执行 3. 确保用户停止输入300ms后才触发搜索 #### 3. 框架集成示例 **Vue 中使用**: ```javascript // 方法1: 直接封装 methods: { handleScroll: throttle(function(e) { this.scrollPosition = e.detail.scrollTop }, 200) } // 方法2: 使用lodash import { throttle } from 'lodash'; export default { methods: { handleScroll: throttle(function(e) { // ... }, 200) } } ``` **React 中使用**: ```javascript import { useRef, useCallback } from 'react'; function useDebounce(fn, delay) { const timerRef = useRef(); return useCallback((...args) => { clearTimeout(timerRef.current); timerRef.current = setTimeout(() => { fn(...args); }, delay); }, [fn, delay]); } // 组件中使用 function SearchComponent() { const handleSearch = useDebounce((query) => { // 搜索逻辑 }, 300); return <input onChange={(e) => handleSearch(e.target.value)} />; } ``` #### 4. 性能优化建议 1. **时间间隔选择**: - 滚动/拖拽:100-200ms - 输入/搜索:300-500ms - 按钮点击:150-250ms 2. **内存管理**: ```javascript // 组件卸载时清除定时器(React示例) useEffect(() => { return () => clearTimeout(timerRef.current); }, []); ``` 3. **高级实现(首次立即执行)**: ```javascript function throttle(fn, delay, immediate = true) { let lastCall = 0; return function(...args) { const now = Date.now(); if (immediate || now - lastCall >= delay) { fn.apply(this, args); lastCall = now; } }; } ``` #### 5. 使用场景对比 | **特性** | 节流(Throttle) | 防抖(Debounce) | |----------------|------------------------|------------------------| | **执行时机** | 固定间隔执行 | 停止触发后执行 | | **适用场景** | 滚动事件、窗口调整 | 搜索建议、表单验证 | | **用户感知** | 响应更及时 | 减少不必要的操作 | | **性能影响** | 中等 | 较低 | | **典型延迟** | 100-200ms | 300-500ms | ### 关键要点总结 1. **节流**保证在密集事件中均匀执行回调,避免卡顿[^1] 2. **防抖**确保只在事件停止后才执行,减少冗余操作[^3] 3. 框架中使用时注意`this`指向问题(避免箭头函数) 4. 组件卸载时需清理定时器防止内存泄漏[^4] 5. 根据场景需求选择合适的时间间隔 > 实际项目中,建议使用Lodash的[_.throttle](https://lodash.com/docs/4.17.15#throttle)[_.debounce](https://lodash.com/docs/4.17.15#debounce)实现,它们包含边缘情况处理取消功能等高级特性[^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端小小白zyw

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值