setInterval每秒执行一次完整教程新手常见坑导致内存泄漏时钟和倒计时实现方法
为什么这个教程值得你看
我第一次接触 setInterval 的时候,是个刚入行半年的前端小白。那时候我以为只要把代码写进去就万事大吉了,结果页面上的数字从1跳到9999,内存占用飙升,浏览器直接卡死。那一刻我才意识到,原来这个看似简单的API背后藏着这么多坑。
这篇文章我会用大白话把所有问题都讲清楚,保证你看完之后不仅会用,还能避开那些能让项目直接崩掉的经典错误。
setInterval 到底是什么
用一句人话来说,setInterval 就是一个定时器,它会让你的代码每隔一段时间就执行一次。这个”一段时间”的单位是毫秒,1000毫秒等于1秒。
// 最简单的写法:每秒执行一次
setInterval(function() {
console.log("我在每秒执行一次");
}, 1000);
这行代码的意思是:每隔1000毫秒(也就是1秒),在控制台打印一句话。就这么简单。
但真正的项目里,我们可不会只打印一句话这么简单。
基本用法详解
基础语法结构
var timerId = setInterval(callback, delay, param1, param2, ...);
- callback:每次定时要执行的函数
- delay:间隔时间(毫秒)
- param1, param2:可选参数,会传递给回调函数
- 返回值:一个唯一的定时器ID,用来停止这个定时器
三种写法
写法一:匿名函数(最常用)
var count = 0;
var timer = setInterval(function() {
count++;
console.log("第" + count + "次执行");
// 执行10次后停止
if (count >= 10) {
clearInterval(timer);
console.log("定时器已停止");
}
}, 1000);
写法二:命名函数(方便管理)
function sayHello() {
console.log("你好,世界!");
}
// 把这个命名函数传给 setInterval
var timer = setInterval(sayHello, 1000);
// 想停止的时候
clearInterval(timer);
写法三:带参数传递
function greet(name, age) {
console.log("你好," + name + ",你今年" + age + "岁");
}
// 传递参数
var timer = setInterval(greet, 1000, "小明", 18);
结合 jQuery 使用
虽然 setInterval 是 JavaScript 的原生API,跟 jQuery 没关系,但在实际项目中我们经常配合 jQuery 一起用。
用 jQuery 更新 DOM
// 每秒更新页面上的数字
var counter = 0;
var $counter = $("#counter"); // 获取元素
var timer = setInterval(function() {
counter++;
$counter.text(counter); // 用 jQuery 更新文本
if (counter >= 10) {
clearInterval(timer);
}
}, 1000);
更实用的例子:动态数据刷新
function refreshData() {
$.ajax({
url: "/api/data",
success: function(data) {
$("#dataContainer").html(data.html);
console.log("数据已刷新");
},
error: function() {
console.log("刷新失败");
}
});
}
// 每5秒自动刷新数据
var refreshTimer = setInterval(refreshData, 5000);
// 用户点击按钮时可以停止刷新
$("#stopRefresh").on("click", function() {
clearInterval(refreshTimer);
});
⚠️ 新手必踩的坑
坑一:忘记清除定时器导致内存泄漏
这是最常见的问题。很多新手只记得创建定时器,却忘了销毁它。
// ❌ 错误写法:创建了定时器但永远不清除
function startClock() {
setInterval(function() {
console.log("时钟运行中...");
}, 1000);
}
// 每次调用这个函数都会创建一个新的定时器
// 如果你在一个按钮点击事件里调用它,点10次就有10个定时器在跑
// 这会造成严重的内存泄漏
正确的做法:
// ✅ 正确写法:保存定时器ID,需要时清除
var clockTimer = null;
function startClock() {
// 先清除可能存在的旧定时器
if (clockTimer !== null) {
clearInterval(clockTimer);
}
clockTimer = setInterval(function() {
console.log("时钟运行中...");
}, 1000);
}
function stopClock() {
if (clockTimer !== null) {
clearInterval(clockTimer);
clockTimer = null; // 记得置空
}
}
坑二:在循环里创建定时器
// ❌ 错误:在循环里创建定时器
for (var i = 0; i < 100; i++) {
setInterval(function() {
console.log(i); // 输出的永远是100
}, 1000);
}
// 会创建100个定时器,每个都在1秒后执行
// 而且输出的i永远是100,因为闭包问题
正确做法:
// ✅ 正确:使用IIFE创建独立作用域
for (var i = 0; i < 100; i++) {
(function(index) {
setInterval(function() {
console.log(index); // 输出0到99
}, 1000);
})(i);
}
坑三:定时器回调函数里有依赖问题
// ❌ 错误:this指向问题
var counter = {
count: 0,
start: function() {
setInterval(function() {
// 这里的this指向window,不是counter对象
this.count++;
console.log(this.count);
}, 1000);
}
};
counter.start(); // 输出NaN
正确做法:
// ✅ 正确:用变量保存this
var counter = {
count: 0,
start: function() {
var self = this; // 保存正确的this
setInterval(function() {
self.count++;
console.log(self.count);
}, 1000);
}
};
counter.start(); // 正确输出1, 2, 3...
坑四:清除定时器时变量作用域问题
// ❌ 错误:变量在函数外部无法访问
function startTimer() {
var timer = setInterval(function() {
console.log("计时中");
}, 1000);
}
function stopTimer() {
clearInterval(timer); // ReferenceError: timer is not defined
}
正确做法:
// ✅ 正确:变量声明在作用域共享的位置
var timer = null;
function startTimer() {
timer = setInterval(function() {
console.log("计时中");
}, 1000);
}
function stopTimer() {
if (timer !== null) {
clearInterval(timer);
timer = null;
}
}
时钟实现完整教程
简单数字时钟
<!DOCTYPE html>
<html>
<head>
<title>数字时钟</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<style>
#clock {
font-size: 48px;
font-family: "Courier New", monospace;
text-align: center;
margin-top: 100px;
color: #333;
}
</style>
</head>
<body>
<div id="clock">00:00:00</div>
<script>
var clockTimer = null;
function updateClock() {
var now = new Date();
var hours = now.getHours();
var minutes = now.getMinutes();
var seconds = now.getSeconds();
// 补齐两位数
hours = hours < 10 ? "0" + hours : hours;
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
// 用jQuery更新显示
$("#clock").text(hours + ":" + minutes + ":" + seconds);
}
// 启动时钟
function startClock() {
// 先清除可能存在的定时器
if (clockTimer !== null) {
clearInterval(clockTimer);
}
// 立即执行一次,避免1秒延迟
updateClock();
// 每秒更新
clockTimer = setInterval(updateClock, 1000);
console.log("时钟已启动");
}
// 停止时钟
function stopClock() {
if (clockTimer !== null) {
clearInterval(clockTimer);
clockTimer = null;
console.log("时钟已停止");
}
}
// 页面加载时启动
$(document).ready(function() {
startClock();
});
</script>
</body>
</html>
带日期显示的时钟
function updateDateTime() {
var now = new Date();
var year = now.getFullYear();
var month = now.getMonth() + 1; // 月份从0开始
var day = now.getDate();
var hours = now.getHours();
var minutes = now.getMinutes();
var seconds = now.getSeconds();
// 补齐两位数
month = month < 10 ? "0" + month : month;
day = day < 10 ? "0" + day : day;
hours = hours < 10 ? "0" + hours : hours;
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
// 更新日期时间
$("#date").text(year + "年" + month + "月" + day + "日");
$("#time").text(hours + ":" + minutes + ":" + seconds);
}
var datetimeTimer = null;
function startDateTime() {
if (datetimeTimer !== null) {
clearInterval(datetimeTimer);
}
updateDateTime();
datetimeTimer = setInterval(updateDateTime, 1000);
}
function stopDateTime() {
if (datetimeTimer !== null) {
clearInterval(datetimeTimer);
datetimeTimer = null;
}
}
倒计时实现完整教程
简单倒计时
<!DOCTYPE html>
<html>
<head>
<title>倒计时</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<style>
.countdown-container {
text-align: center;
margin-top: 100px;
}
#countdown {
font-size: 72px;
font-family: "Arial", sans-serif;
color: #e74c3c;
font-weight: bold;
}
button {
margin: 10px;
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
}
</style>
</head>
<body>
<div class="countdown-container">
<div id="countdown">00:00</div>
<br>
<button id="startBtn">开始</button>
<button id="pauseBtn">暂停</button>
<button id="resetBtn">重置</button>
</div>
<script>
var countdownTimer = null;
var totalSeconds = 0; // 总秒数
var remainingSeconds = 0; // 剩余秒数
var isRunning = false;
// 格式化时间显示
function formatTime(seconds) {
var mins = Math.floor(seconds / 60);
var secs = seconds % 60;
mins = mins < 10 ? "0" + mins : mins;
secs = secs < 10 ? "0" + secs : secs;
return mins + ":" + secs;
}
// 更新倒计时显示
function updateCountdown() {
if (remainingSeconds > 0) {
remainingSeconds--;
$("#countdown").text(formatTime(remainingSeconds));
// 最后10秒变红色闪烁效果
if (remainingSeconds <= 10 && remainingSeconds > 0) {
$("#countdown").css("color", "#c0392b");
}
// 倒计时结束
if (remainingSeconds === 0) {
clearInterval(countdownTimer);
countdownTimer = null;
isRunning = false;
alert("时间到!");
}
}
}
// 开始倒计时
function startCountdown() {
if (isRunning) return; // 已经在运行就不重复启动
if (totalSeconds === 0) {
// 如果没有设置时间,默认30秒
totalSeconds = 30;
}
isRunning = true;
remainingSeconds = totalSeconds;
// 立即更新一次
$("#countdown").text(formatTime(remainingSeconds));
// 开始倒计时
countdownTimer = setInterval(updateCountdown, 1000);
}
// 暂停倒计时
function pauseCountdown() {
if (!isRunning) return;
if (countdownTimer !== null) {
clearInterval(countdownTimer);
countdownTimer = null;
}
isRunning = false;
}
// 重置倒计时
function resetCountdown() {
pauseCountdown();
if (totalSeconds === 0) {
totalSeconds = 30; // 默认值
}
remainingSeconds = totalSeconds;
$("#countdown").text(formatTime(remainingSeconds)).css("color", "#e74c3c");
}
// 绑定按钮事件
$("#startBtn").on("click", startCountdown);
$("#pauseBtn").on("click", pauseCountdown);
$("#resetBtn").on("click", resetCountdown);
// 初始化显示
$(document).ready(function() {
totalSeconds = 30;
remainingSeconds = totalSeconds;
$("#countdown").text(formatTime(remainingSeconds));
});
</script>
</body>
</html>
可自定义时间的倒计时
var customTimer = null;
var customTotalSeconds = 0;
var customRemainingSeconds = 0;
var customIsRunning = false;
function startCustomCountdown(minutes) {
if (customTimer !== null) {
clearInterval(customTimer);
}
customTotalSeconds = minutes * 60;
customRemainingSeconds = customTotalSeconds;
customIsRunning = true;
updateCustomDisplay();
customTimer = setInterval(function() {
if (customRemainingSeconds > 0) {
customRemainingSeconds--;
updateCustomDisplay();
if (customRemainingSeconds === 0) {
clearInterval(customTimer);
customTimer = null;
customIsRunning = false;
console.log("自定义倒计时结束!");
}
}
}, 1000);
}
function stopCustomCountdown() {
if (customTimer !== null) {
clearInterval(customTimer);
customTimer = null;
}
customIsRunning = false;
}
function resetCustomCountdown(minutes) {
stopCustomCountdown();
customTotalSeconds = minutes * 60;
customRemainingSeconds = customTotalSeconds;
updateCustomDisplay();
}
function updateCustomDisplay() {
var display = formatTime(customRemainingSeconds);
$("#customCountdown").text(display);
}
定时器性能优化
避免闭包陷阱
// ❌ 错误:闭包捕获循环变量
var items = ["item1", "item2", "item3"];
for (var i = 0; i < items.length; i++) {
setInterval(function() {
console.log(items[i]); // 永远是undefined
}, 1000);
}
// ✅ 正确:使用IIFE创建独立作用域
for (var i = 0; i < items.length; i++) {
(function(index) {
setInterval(function() {
console.log(items[index]); // 正确输出
}, 1000);
})(i);
}
使用 requestAnimationFrame 代替 setInterval
对于动画相关的需求,requestAnimationFrame 比 setInterval 更合适:
// ❌ 用 setInterval 做动画(不推荐)
setInterval(function() {
var $box = $("#box");
var currentLeft = parseInt($box.css("left")) || 0;
$box.css("left", currentLeft + 5 + "px");
}, 16); // 约60fps
// ✅ 用 requestAnimationFrame 做动画(推荐)
function animate() {
var $box = $("#box");
var currentLeft = parseInt($box.css("left")) || 0;
$box.css("left", currentLeft + 5 + "px");
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
节流和防抖配合定时器
var scrollTimer = null;
$(window).on("scroll", function() {
// 清除之前的定时器
if (scrollTimer !== null) {
clearTimeout(scrollTimer);
}
// 重新设置定时器(防抖)
scrollTimer = setTimeout(function() {
console.log("滚动结束");
scrollTimer = null;
}, 300);
});
实际应用案例
实时时钟显示
var clockTimer = null;
function updateRealTimeClock() {
var now = new Date();
var timeStr = now.toLocaleTimeString('zh-CN', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
var dateStr = now.toLocaleDateString('zh-CN');
$("#realTimeClock").text(timeStr);
$("#realTimeDate").text(dateStr);
}
function startRealTimeClock() {
if (clockTimer !== null) {
clearInterval(clockTimer);
}
updateRealTimeClock();
clockTimer = setInterval(updateRealTimeClock, 1000);
}
function stopRealTimeClock() {
if (clockTimer !== null) {
clearInterval(clockTimer);
clockTimer = null;
}
}
自动保存草稿
var autoSaveTimer = null;
var lastContent = "";
function startAutoSave() {
// 先清除可能存在的定时器
if (autoSaveTimer !== null) {
clearInterval(autoSaveTimer);
}
// 每30秒自动保存一次
autoSaveTimer = setInterval(function() {
var currentContent = $("#articleContent").val();
// 内容变化时才保存,避免无效请求
if (currentContent !== lastContent) {
$.ajax({
url: "/api/save",
method: "POST",
data: {
content: currentContent
},
success: function(response) {
console.log("自动保存成功");
$("#saveStatus").text("已保存 " + new Date().toLocaleTimeString());
lastContent = currentContent;
},
error: function() {
console.log("自动保存失败");
$("#saveStatus").text("保存失败,请重试");
}
});
}
}, 30000);
}
function stopAutoSave() {
if (autoSaveTimer !== null) {
clearInterval(autoSaveTimer);
autoSaveTimer = null;
}
}
在线状态检测
var statusTimer = null;
function checkOnlineStatus() {
$.ajax({
url: "/api/status",
success: function(response) {
if (response.online) {
$("#statusIndicator").css("background-color", "#2ecc71");
$("#statusText").text("在线");
} else {
$("#statusIndicator").css("background-color", "#e74c3c");
$("#statusText").text("离线");
}
}
});
}
function startStatusCheck() {
if (statusTimer !== null) {
clearInterval(statusTimer);
}
checkOnlineStatus();
statusTimer = setInterval(checkOnlineStatus, 5000); // 每5秒检查一次
}
function stopStatusCheck() {
if (statusTimer !== null) {
clearInterval(statusTimer);
statusTimer = null;
}
}
总结
掌握了这些内容,你已经能熟练运用 setInterval 了。记住几个关键点:
- 创建定时器时保存ID:
var timer = setInterval(...) - 不需要时及时清除:
clearInterval(timer) - 注意this指向问题:用
self或箭头函数 - 循环里创建定时器要注意作用域:用IIFE或箭头函数
- 动画优先用 requestAnimationFrame:性能更好
- 页面卸载时要清除定时器:避免内存泄漏
希望这篇教程对你有帮助。如果还有问题,欢迎随时问我。记住,定时器这东西用好了是利器,用不好就是地雷,千万别大意。
