闭包是实现节流最自然、最安全的载体——它能封装状态(如上一次执行时间、是否已排队)、避免全局变量污染,同时保证每次事件回调都持有独立上下文。

闭包是实现节流最自然、最安全的载体——它能封装状态(如上一次执行时间、是否已排队)、避免全局变量污染,同时保证每次事件回调都持有独立上下文。在 scroll 和 resize 这类高频事件中,用闭包封装节流逻辑,既清晰又可复用。
基于时间戳的闭包节流(适合简单同步操作)
核心是记录上次执行时间,仅当间隔 ≥ 16ms 才允许下一次执行。闭包保存 lastTime,无需依赖外部变量:
- 每次触发时读取 Date.now(),与闭包内缓存的 lastTime 比较
- 满足间隔才执行目标函数,并立即更新 lastTime
- 不依赖 requestAnimationFrame,适合轻量判断(如 class 切换、状态标记)
示例:
const throttle = (fn, delay = 16) => {<br> let lastTime = 0;<br> return function(...args) {<br> const now = Date.now();<br> if (now - lastTime >= delay) {<br> fn.apply(this, args);<br> lastTime = now;<br> }<br> };<br>};<br>window.addEventListener('scroll', throttle(() => {<br> document.body.classList.toggle('scrolled', window.scrollY > 10);<br>}, 16));
基于 requestAnimationFrame 的闭包节流(真·60FPS 推荐)
RAF 节流不是“加个定时器”,而是把业务逻辑精准对齐浏览器渲染帧。闭包管理 isQueued 标志,确保同一帧只注册一次 RAF:
- scroll 回调只做两件事:更新滚动位置缓存、调用 requestAnimationFrame
- RAF 回调里执行真实 DOM 计算和更新,且执行后重置 isQueued = false
- 天然适配不同刷新率屏幕(60Hz/90Hz/120Hz),不硬编码 16ms
示例:
const createRAFThrottle = (fn) => {<br> let isQueued = false;<br> let scrollTop = 0;<br> const update = () => {<br> isQueued = false;<br> fn(scrollTop);<br> };<br> return function() {<br> scrollTop = window.pageYOffset || document.documentElement.scrollTop;<br> if (!isQueued) {<br> isQueued = true;<br> requestAnimationFrame(update);<br> }<br> };<br>};<br>window.addEventListener('scroll', createRAFThrottle((pos) => {<br> // ✅ 安全区域计算、虚拟列表索引更新、transform 设置<br> updateSticky(pos);<br> loadVisibleImages(pos);<br>}));
resize 场景下的双闭包节流(防抖+节流混合)
resize 比 scroll 更不可控:浏览器可能连续触发数十次,且常伴随布局重排。单一层节流易漏关键尺寸变化。推荐「防抖保终态 + 节流保过程」双闭包策略:
- 外层闭包用 debounce 确保窗口停止调整后执行最终适配(如重绘 Canvas、重设栅格)
- 内层闭包用 throttle 在调整过程中每 50ms 触发一次轻量响应(如更新缩放比例提示、调整容器透明度)
- 两个闭包各自维护独立状态,互不干扰
示例:
const debouncedResize = debounce(() => {<br> chart.resize(); // 重量级重绘<br>}, 200);<br><br>const throttledPreview = throttle(() => {<br> previewEl.style.opacity = Math.min(1, window.innerWidth / 1920);<br>}, 50);<br><br>window.addEventListener('resize', () => {<br> debouncedResize();<br> throttledPreview();<br>});
Vue/React 中的闭包节流封装(组件级隔离)
在框架中,节流函数应绑定到组件实例生命周期,避免跨组件状态污染。利用闭包 + useEffect 或 onMounted 封装,让每个组件拥有专属节流上下文:
- Vue 中在 onMounted 内创建节流函数并绑定,onBeforeUnmount 清理
- React 中用 useEffect 返回清理函数,闭包捕获最新 props 和 ref
- 避免将节流函数写在 setup 或组件顶层——会导致所有实例共享同一 lastTime
Vue 示例(组合式 API):
setup() {<br> const containerRef = ref(null);<br> onMounted(() => {<br> const handleScroll = throttle(() => {<br> if (!containerRef.value) return;<br> const rect = containerRef.value.getBoundingClientRect();<br> emit('inview', rect.top }, 32); // 放宽至 32ms,降低 CPU 占用<br> window.addEventListener('scroll', handleScroll);<br> onBeforeUnmount(() => {<br> window.removeEventListener('scroll', handleScroll);<br> });<br> });<br> return { containerRef };<br>}










