函数节流的核心是限制函数在单位时间内只能执行一次,常用于优化高频事件的性能。与防抖不同,节流采用“定时定量”执行策略,无论事件触发多频繁,每隔一段时间必定执行一次,适用于需要周期性响应的场景。防抖则等待事件流停止后才执行,适合只关注最终状态的操作。节流典型应用场景包括滚动事件(如懒加载、滚动加载)、窗口缩放、鼠标移动(如拖拽、绘图)和按钮防重复点击等。一个基础节流实现使用settimeout配合标志位控制执行频率,而更健壮的版本支持leading(首次立即执行)和trailing(末次补执行)选项,并提供cancel方法以手动清除状态,确保灵活性和可控性。因此,掌握节流技术是前端性能优化的关键技能之一。

函数节流,简单来说,就是限制一个函数在特定时间内只能执行一次。它不像防抖那样是等事件完全停止才执行,节流更像是“定时定量”地执行,不管事件触发多频繁,我每隔一段时间就处理一次。这在很多需要控制执行频率的场景下,能显著提升页面性能和用户体验。
实现函数节流的核心思路,是利用一个定时器或者时间戳来控制函数的执行频率。最常见的做法是使用一个标志位或者一个定时器ID,确保在设定的延迟时间内,函数不会被重复执行。
这是一个基于
setTimeout
function throttle(func, delay) {
let timeoutId = null; // 用于存储定时器ID
return function(...args) { // 返回一个新函数,接收所有参数
// 如果当前没有正在等待执行的定时器
if (!timeoutId) {
// 设置一个定时器
timeoutId = setTimeout(() => {
// 定时器触发时,执行原始函数,并确保this上下文和参数正确传递
func.apply(this, args);
// 执行完毕后,清除定时器ID,允许下一次节流触发
timeoutId = null;
}, delay);
}
};
}
// 示例用法:
// const handleScroll = throttle(() => {
// console.log('滚动事件被节流了!');
// }, 200);
// window.addEventListener('scroll', handleScroll);这个实现相对简洁,它的逻辑是:当函数被调用时,如果当前没有待执行的定时器,就设置一个。在定时器期间,即使函数被多次调用,也不会设置新的定时器。只有当定时器执行完毕,
timeoutId
null
说实话,刚开始接触函数优化时,节流和防抖常常让我混淆。它们确实都是为了优化高频事件处理,但侧重点完全不同。
防抖(Debounce) 的核心思想是“我等你停下来”。它只会在事件连续触发的间隔超过设定的时间后,才执行一次函数。想象一下你快速输入搜索框内容,防抖会等到你停止输入几百毫秒后,才去发送搜索请求。它关注的是“最终结果”。
节流(Throttle) 的核心思想是“我每隔一段时间就执行一次”。不管事件触发多频繁,它都会保证在设定的时间间隔内,函数最多只执行一次。比如你拖拽一个元素,节流会让你在拖拽过程中,每隔100毫秒更新一次元素位置,而不是每移动一个像素就更新一次。它关注的是“周期性执行”。
什么时候用节流,什么时候用防抖?
用防抖的场景:
input
用节流的场景:
函数节流在前端开发中简直是性能优化的“瑞士军刀”,用得好能解决很多性能瓶颈。
php配置文件php.ini的中文注释版是一本由多位作者编著的有关PHP内部实现的开源书籍。从环境准备到代码实现,从实现过程到细节延展,从变量、函数、对象到内存、Zend虚拟机…… 如此种种,道尽PHP之风流。
376
滚动事件(scroll
窗口调整事件(resize
鼠标移动事件(mousemove
mousemove
按钮重复点击:
输入框实时搜索建议:
在我看来,掌握节流和防抖,是前端工程师优化用户体验和应用性能的基本功。很多时候,一个看似简单的交互卡顿,背后可能就是因为没有合理地使用这些技巧。
上面给出的节流函数是一个基础版本,它只实现了“尾部执行”(trailing edge)。但在实际项目中,我们经常需要更灵活的控制,比如希望第一次事件触发时就立即执行(leading edge),或者在事件停止后仍然保证最后一次执行(trailing edge),甚至需要一个取消节流的方法。
一个更健壮的节流函数通常会包含
leading
trailing
this
function throttle(func, wait, options = {}) {
let timeoutId;
let lastArgs;
let lastThis;
let lastResult; // 存储最后一次执行的结果
let lastCallTime = 0; // 记录上次函数实际执行的时间戳
// 默认开启前沿和后沿执行
const leading = options.leading === undefined ? true : !!options.leading;
const trailing = options.trailing === undefined ? true : !!options.trailing;
// 内部执行函数,处理上下文和参数
const invokeFunc = (time) => {
lastResult = func.apply(lastThis, lastArgs);
lastCallTime = time; // 更新上次执行时间
};
const throttled = function(...args) {
const now = Date.now(); // 当前时间
lastArgs = args;
lastThis = this;
// 如果是第一次调用,并且leading为false,则将lastCallTime设置为当前时间
// 这样可以跳过第一次的立即执行
if (!lastCallTime && !leading) {
lastCallTime = now;
}
// 计算距离下次执行还需要等待的时间
const remaining = wait - (now - lastCallTime);
// 如果时间已到或者系统时间被修改(remaining可能为负数或超过wait)
if (remaining <= 0 || remaining > wait) {
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
lastCallTime = now; // 更新执行时间
invokeFunc(now); // 立即执行
} else if (!timeoutId && trailing) { // 如果没有正在等待的定时器,并且开启了trailing
// 设置一个定时器,在剩余时间后执行
timeoutId = setTimeout(() => {
lastCallTime = Date.now(); // 更新执行时间为定时器触发时的时间
timeoutId = null;
invokeFunc(lastCallTime);
}, remaining);
}
return lastResult; // 返回最后一次执行的结果
};
// 添加一个取消节流的方法
throttled.cancel = () => {
clearTimeout(timeoutId);
timeoutId = null;
lastCallTime = 0;
};
return throttled;
}
// 示例用法:
// // 默认模式:第一次立即执行,之后每200ms最多执行一次,事件停止后若有待执行的也会执行最后一次
// const throttledScroll = throttle(() => console.log('Scroll!'), 200);
// window.addEventListener('scroll', throttledScroll);
// // 只在事件停止后执行(类似防抖,但仍然有节流的效果,即在wait时间内不会重复执行)
// const throttledResizeTrailing = throttle(() => console.log('Resize trailing!'), 300, { leading: false, trailing: true });
// window.addEventListener('resize', throttledResizeTrailing);
// // 第一次立即执行,之后每300ms最多执行一次,但事件停止后不再执行(如果最后一次触发在等待期内)
// const throttledClickLeading = throttle(() => console.log('Click!'), 300, { leading: true, trailing: false });
// document.getElementById('myButton').addEventListener('click', throttledClickLeading);这个更复杂的实现,通过
lastCallTime
remaining
leading: true
trailing: true
cancel
它考虑了更多实际应用中的需求,比如你希望用户第一次点击按钮就立即响应,但在冷却期内不再响应;或者在拖拽开始时立即更新位置,但拖拽停止后也要保证最终位置被更新。这种灵活的控制,让节流函数变得更加实用和强大。编写这种函数,真的需要对时间、闭包和函数上下文有比较深入的理解。
以上就是js 如何实现函数节流的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号