如何正确清除赋值给全局变量的setInterval定时器?
问题根源
你的代码clearInterval不生效是两个核心问题叠加导致的:
- 定时器ID丢失:你用
var声明的intervalCursor不是全局变量,作用域被限制在MutationObserver的回调函数内。更致命的是,每次类名变化、满足进入if分支的条件时,都会重新执行setInterval生成新的定时器,新ID会直接覆盖变量里存储的旧ID,之前创建的定时器ID彻底丢失,后续调用clearInterval只能清除最后一次生成的定时器,更早创建的定时器因为没有留存ID,根本无法被清除,会持续在后台运行。 - 缺少重复初始化校验:每次进入if分支都会给window绑定一个新的
mousemove事件监听,从未做解绑处理,触发几次符合条件的类名变更,就会绑定多少个重复的监听,既浪费性能,也会干扰坐标值的更新。另外你在update方法里直接给l、t赋值没有加变量声明,这两个值会泄漏成全局变量,也属于代码隐患。
修复后的完整实现
把定时器ID、事件处理函数等状态值提到Observer外层作用域留存,每次进入初始化分支前先清理旧的定时器和事件监听,避免重复创建资源:
const runPreview = document.querySelector('.projects'); // 核心状态提到外层作用域,避免ID、事件引用丢失 let intervalCursor = null; let mousemoveHandler = null; let mouseX = window.innerWidth / 2; let mouseY = window.innerHeight / 2; let projectPreview = null; // 工具函数全局定义,不用每次初始化重复声明 function lerp(start, end, amt) { return (1 - amt) * start + amt * end } new MutationObserver((mutations) => { if (mutations[0].attributeName === 'class') { console.log('Project list class changed'); if ($('html').hasClass('no-touchevents') && $('.projects').hasClass('run-preview')) { // 进入分支先清理旧资源,避免重复生成定时器、重复绑定事件 if (intervalCursor) clearInterval(intervalCursor); if (mousemoveHandler) $(window).off('mousemove', mousemoveHandler); // 初始化预览元素配置 projectPreview = { el: $('.image-container'), x: window.innerWidth / 2, y: window.innerHeight / 2, w: 300, h: 300, update: function () { // 加变量声明,避免污染全局 const l = this.x - this.w / 2; const t = this.y - this.h / 2; this.el.css({ 'transform': 'translate3d(' + l + 'px,' + t + 'px, 0)' }); } } // 保存事件处理函数引用,方便后续解绑 mousemoveHandler = function (e) { mouseX = e.clientX; mouseY = e.clientY; } $(window).on('mousemove', mousemoveHandler); // 动画帧逻辑 function move() { projectPreview.x = lerp(projectPreview.x, mouseX, 0.1); projectPreview.y = lerp(projectPreview.y, mouseY, 0.1); projectPreview.update(); } // 启动定时器,留存ID intervalCursor = setInterval(move, 1000 / 60); } else if (!$('.projects').hasClass('run-preview')) { // 退出分支时同步清理定时器、解绑事件 if (intervalCursor) { clearInterval(intervalCursor); intervalCursor = null; } if (mousemoveHandler) { $(window).off('mousemove', mousemoveHandler); mousemoveHandler = null; } } } }).observe(runPreview, { attributes: true });
内容的提问来源于stack exchange,提问作者Ira
相关产品推荐
相关产品推荐

