JavaScript-防抖和节流
在前端开发中,我们经常会遇到一些高频触发的事件,比如 scroll、resize、input 等。如果对这些事件的处理函数不加以限制,可能会导致性能问题,甚至页面卡顿。为了解决这类问题,防抖(Debounce)和节流(Throttle)就成为了我们优化高频事件的得力工具。接下来,我们就深入了解一下这两个概念及其实现方式。
一、防抖(Debounce)
1.概念
防抖的核心思想是:当一个事件被频繁触发时,设定一个时间阈值,在这个时间阈值内,如果事件再次被触发,就重新计时,只有当超过设定的时间阈值,且没有新的事件触发时,才执行对应的处理函数。
简单来说,就是将多次连续的事件触发合并为一次延迟触发(即:指连续触发事件但是在设定的一段时间内中只执行最后一次)。
例如:设定1000毫秒执行,当你触发事件了,它会1000毫秒后执行,但是在还剩500毫秒的时候你又触发了事件,那就会重新开始1000毫秒之后再执行
记忆核心:从新开始(例如:王者回城)
2.应用场景
搜索框实时搜索:用户在搜索框中输入内容时,每输入一个字符就触发搜索请求会浪费资源。使用防抖,当用户停止输入一段时间后,再发起搜索请求,既节省资源又不影响用户体验。
按钮防重复点击:防止用户在短时间内多次点击按钮,避免重复提交表单或执行不必要的操作。
文本编辑器实时保存
3.代码实现
⑴.HTML 代码
<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="stylesheet" href="styles.css"> <!-- 引入外部CSS样式文件,用于美化页面,这里假设存在styles.css文件 --><title>防抖测试案例</title>
</head><body><h1>防抖测试示例</h1><input type="text" id="searchInput" placeholder="请输入搜索内容"> <!-- 搜索框 --><div id="result"></div> <!-- 用于显示搜索结果的区域 --><script src="script.js"></script> <!-- 引入包含防抖函数和相关逻辑的JavaScript文件 -->
</body></html>
⑵CSS 代码(styles.css)
body {font-family: Arial, sans-serif;text-align: center;background-color: #f4f4f4;
}h1 {color: #333;
}#searchInput {padding: 10px;width: 300px;border: 1px solid #ccc;border-radius: 5px;
}#result {margin-top: 20px;padding: 10px;background-color: #fff;border: 1px solid #ccc;border-radius: 5px;min-height: 50px;
}
⑶JavaScript 代码(script.js)
// 防抖函数定义
function debounce(func, delay) {let timer; // 用于存储定时器的变量,利用闭包特性在多次调用时保持状态return function() {const context = this; // 保存函数调用时的this指向const args = arguments; // 获取调用函数时传入的参数clearTimeout(timer); // 清除之前设置的定时器,如果在延迟时间内再次触发,就取消之前的延迟执行计划timer = setTimeout(() => {func.apply(context, args); // 在延迟时间结束且没有新触发时,执行传入的函数,并传递正确的this和参数}, delay);};
}// 模拟搜索函数,实际应用中这里应该是发起真实的搜索请求
function search() {const searchInput = document.getElementById('searchInput');const result = document.getElementById('result');const inputValue = searchInput.value;if (inputValue.trim() === '') {result.innerHTML = '请输入搜索内容';} else {result.innerHTML = `正在搜索 "${inputValue}"...`;// 这里可以添加实际的异步搜索请求代码,例如使用fetch或axios// 示例:fetch(`https://api.example.com/search?q=${inputValue}`).then(response => response.json()).then(data => {// result.innerHTML = JSON.stringify(data);// });}
}// 创建防抖后的搜索函数,延迟时间设置为500毫秒
const debouncedSearch = debounce(search, 500);// 为搜索框的input事件绑定防抖后的搜索函数
document.getElementById('searchInput').addEventListener('input', debouncedSearch);
function debounce(func, delay) {let timer;return function() {const context = this;const args = arguments;clearTimeout(timer);timer = setTimeout(() => {func.apply(context, args);}, delay);};
}// 使用示例
const debouncedSearch = debounce(() => {console.log('执行搜索操作');
}, 500);document.getElementById('searchInput').addEventListener('input', debouncedSearch);
在上述代码中,debounce 函数接收两个参数:要执行的函数 func 和延迟时间 delay。通过闭包保存一个定时器 timer,每次事件触发时,先清除之前的定时器,然后重新设置一个新的定时器。只有当延迟时间内没有新的事件触发,定时器到期时,才会执行传入的函数 func。
二、节流(Throttle)
1.概念
节流的原理是:规定在一个单位时间内,只能触发一次事件处理函数。如果在这个单位时间内有多次事件触发,也只会执行一次。它就像一个阀门,控制事件处理函数的执行频率,保证在一定时间间隔内,函数最多执行一次。
例如:技能冷却,规定时间内只能触发一次,就触发一次,不断点击也没用
2.应用场景
- 高频事件:例如,快速点击、鼠标滑动、resize事件、scroll事件
- 下拉加载
- 视频播放记录时间等
3.代码实现
// 定义节流函数throttle,接收两个参数:要执行的函数func和时间间隔delay
function throttle(func, delay) {// 声明一个变量timer用于存储定时器,利用闭包特性在多次调用时保持状态let timer;// 返回一个新的函数,该函数会在事件触发时被调用return function() {// 保存函数调用时的this指向,确保传入的func执行时this的正确性const context = this;// 获取调用该函数时传入的参数,以便后续传递给funcconst args = arguments;// 如果timer为null,说明距离上次执行func已经过去足够的时间if (!timer) {// 执行传入的函数func,并将context作为this值,args作为参数传递进去func.apply(context, args);// 设置一个新的定时器,在delay毫秒后执行回调函数timer = setTimeout(() => {// 定时器执行完毕后,将timer置为null,允许下次执行functimer = null;}, delay);}};
}// 使用示例:创建一个节流后的函数throttledScroll
// 传入的函数是一个匿名函数,功能是在控制台打印'执行滚动事件处理'
// 时间间隔设置为200毫秒,即每200毫秒最多执行一次该匿名函数
const throttledScroll = throttle(() => {console.log('执行滚动事件处理');
}, 200);// 将节流后的函数throttledScroll绑定到窗口的scroll事件上
// 这样在窗口滚动过程中,滚动事件处理函数会按照节流设置的频率执行
window.addEventListener('scroll', throttledScroll);
throttle 函数同样接收要执行的函数 func 和时间间隔 delay。通过判断定时器 timer 是否存在来控制函数的执行。当 timer 为 null 时,说明上一次函数执行已经结束,此时可以执行传入的函数 func,并设置一个新的定时器。在定时器到期前,即使有新的事件触发,也不会再次执行函数,直到定时器结束,timer 重新变为 null。