1098 字
5 分钟
JavaScript 节流

题目描述#

实现 JavaScript 节流函数,要求:

  1. 固定时间间隔内只执行一次函数
  2. 支持立即执行选项(首次触发立即执行)
  3. 支持尾部执行选项(最后一次触发后执行)
  4. 支持取消功能(在等待期间取消执行)
  5. 正确处理函数参数和 this 绑定

要求完整实现节流逻辑,并通过测试用例验证。

解题思路#

  1. 时间戳控制:记录上次执行时间判断是否满足间隔
  2. 计时器管理:使用 setTimeout 实现尾部执行
  3. 立即执行选项:首次触发立即执行函数
  4. 尾部执行选项:最后一次触发后执行函数
  5. 取消机制:提供取消执行的方法
  6. 参数传递:保存函数参数并正确传递

关键洞察#

  1. 频率控制本质:通过时间间隔限制函数执行次数
  2. 双模式互补:时间戳模式保证立即响应,计时器模式保证最终执行
  3. 执行时机分离:头部执行保证即时性,尾部执行保证完整性
  4. 状态管理机制:闭包保存执行状态和时间戳
  5. 参数保存机制:闭包保存参数确保最新值
  6. 上下文绑定:使用 apply 确保 this 正确

代码流程#

flowchart TD 
	A[调用节流函数] --> B{是否首次执行?} 
	B -->|是且立即执行| C[立即执行函数] 
	B -->|否| D{是否在间隔内} 
	D -->|是| E[设置尾部执行] 
	D -->|否| F[立即执行] 
	G[取消操作] --> H[清除计时器]

代码实现#

// 基础防抖
const basicDebounce = debounce(() => {
console.log('Resize event handled');
}, 250);
window.addEventListener('resize', basicDebounce);
// 立即执行防抖
const searchDebounce = debounce((query) => {
console.log(`Searching for: ${query}`);
}, 500, {leading: true});
searchInput.addEventListener('input', (e) => {
searchDebounce(e.target.value);
});
// 取消示例
const cancelableDebounce = debounce(() => {
console.log('This will not run if canceled');
}, 1000);
cancelableDebounce();
setTimeout(cancelableDebounce.cancel, 500); // 取消执行
// Promise支持function throttle(func, wait = 300, options = {}) {
let timerId = null;
let lastArgs = null;
let lastThis = null;
let lastTime = 0;
// 选项默认值处理
const leading = 'leading' in options ? !!options.leading : true;
const trailing = 'trailing' in options ? !!options.trailing : true;
// 执行目标函数
function invokeFunc(time) {
lastTime = time;
return func.apply(lastThis, lastArgs);
}
// 主函数
function throttled(...args) {
const now = Date.now();
lastArgs = args;
lastThis = this;
// 计算剩余等待时间
const remaining = wait - (now - lastTime);
// 首次执行判断
if (remaining <= 0 || remaining > wait) {
// 清除尾部计时器
if (timerId) {
clearTimeout(timerId);
timerId = null;
}
// 满足间隔要求,立即执行
return invokeFunc(now);
}
// 设置尾部执行
if (trailing && !timerId) {
timerId = setTimeout(() => {
timerId = null;
if (trailing && lastArgs) {
return invokeFunc(Date.now());
}
}, remaining);
}
}
// 取消方法
throttled.cancel = function() {
clearTimeout(timerId);
timerId = null;
lastArgs = null;
lastThis = null;
lastTime = 0;
};
// 立即执行当前值
throttled.flush = function() {
if (timerId) {
clearTimeout(timerId);
timerId = null;
return invokeFunc(Date.now());
}
};
return throttled;
}
const promiseDebounce = debounce((value) => {
return Promise.resolve(`Processed: ${value}`);
}, 300);
promiseDebounce('data').then(console.log); // 输出: Processed: data

使用示例#

// 基础节流
const basicThrottle = throttle(() => {
console.log('Scroll event handled');
}, 250);
window.addEventListener('scroll', basicThrottle);
// 立即执行节流
const leadingThrottle = throttle(() => {
console.log('Immediate execution');
}, 500, {leading: true});
button.addEventListener('click', leadingThrottle);
// 尾部执行节流
const trailingThrottle = throttle(() => {
console.log('Trailing execution');
}, 500, {trailing: true});
input.addEventListener('input', trailingThrottle);
// 取消示例
const cancelableThrottle = throttle(() => {
console.log('This will not run if canceled');
}, 1000);
cancelableThrottle();
setTimeout(cancelableThrottle.cancel, 500); // 取消执行

业务场景#

  1. 滚动事件:页面滚动时监听位置变化(如导航栏显示/隐藏)
  2. 鼠标移动:拖拽操作时的元素位置更新
  3. 实时预览:表单输入时实时预览内容(如Markdown编辑器)
  4. 游戏控制:键盘事件处理(如角色移动控制)
  5. 性能监控:定期采集性能指标数据
  6. 动画效果:scroll-linked动画控制(如视差滚动)

性能优势

  • 保证高频事件的核心功能稳定执行
  • 避免过度渲染导致的页面卡顿
  • 减少不必要的计算资源消耗
  • 平衡用户体验与性能消耗
  • 防止接口频繁调用导致服务器压力

相似题目#

  1. 防抖(Debounce)实现(简单) 核心:延迟执行直到事件停止触发
  2. 动画帧节流(中等) 核心:requestAnimationFrame 实现动画控制
  3. 滚动位置监听优化(中等) 核心:IntersectionObserver + 节流
  4. 请求节流控制(困难) 核心:限制接口调用频率(如API限流)
  5. 鼠标跟随效果优化(中等) 核心:mousemove事件节流实现平滑跟随
JavaScript 节流
https://website-truelovings-projects.vercel.app/posts/code/javascript/javascript-节流/
作者
欢迎来到StarSky的网站!
发布于
2024-04-17
许可协议
CC BY-NC-SA 4.0