JavaScript多场景每日双时段倒计时器实现与代码优化求助
优化你的每日双时段倒计时器代码
嘿,看到你已经用switch搞定了倒计时器的功能,真不错!不过我发现代码里有两个需要注意的问题,同时也可以通过提取重复逻辑来大幅精简代码:
隐藏bug:
- 周三(case3)和周五(case5)的判断里,
nowDate.getHours > 10少了括号,应该是nowDate.getHours(),不然这个判断会一直为true,导致逻辑错误; - 全局变量
nowDate只会在页面加载时初始化一次,后续调用getSeconds()时不会更新当前时间,这会让倒计时计算越来越不准。
- 周三(case3)和周五(case5)的判断里,
代码冗余:重复的switch-case和Date创建代码太多,后续修改时段会很麻烦。
优化思路:配置化管理规则
我们可以把一周七天的倒计时规则抽成配置数组,每个元素对应当天的两个目标时段(包含跨天情况),这样就能彻底去掉冗余的switch-case,让代码更清晰、更易维护。同时修复上述bug,优化计时器逻辑。
优化后的完整代码
// 全局变量精简(仅保留必要的计时器引用) let ticker; const DAY_SECONDS = 86400; const WEEK_SECONDS = DAY_SECONDS * 7; // 配置一周七天的倒计时规则:索引0=周日,1=周一...6=周六 // 每个时段格式:[小时, 分钟, 日期偏移(可选,默认0)] const dayConfigs = [ // 周日:17:00(当天)、23:15(当天) [[17, 0], [23, 15]], // 周一:10:00(当天)、23:15(当天) [[10, 0], [23, 15]], // 周二:20:00(当天)、次日2:00 [[20, 0], [2, 0, 1]], // 周三:10:00(当天,2点后)、23:15(当天,10点后) [[10, 0], [23, 15]], // 周四:20:00(当天)、次日2:00 [[20, 0], [2, 0, 1]], // 周五:10:00(当天,2点后)、23:15(当天,10点后) [[10, 0], [23, 15]], // 周六:14:00(当天)、20:00(当天) [[14, 0], [20, 0]] ]; /** * 获取当前应倒计时的目标时间差(秒) */ function getTargetTimeDiff() { const now = new Date(); const dayOfWeek = now.getDay(); const currentHour = now.getHours(); const currentMinute = now.getMinutes(); const [firstPeriod, secondPeriod] = dayConfigs[dayOfWeek]; let targetDate, dateOffset = 0; // 根据不同日期的规则匹配目标时间 switch(dayOfWeek) { case 2: // 周二 case 4: // 周四 if (currentHour < 20) { targetDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), firstPeriod[0], firstPeriod[1]); } else { dateOffset = secondPeriod[2] || 0; targetDate = new Date(now.getFullYear(), now.getMonth(), now.getDate() + dateOffset, secondPeriod[0], secondPeriod[1]); } break; case 3: // 周三 case 5: // 周五 if (currentHour > 2 && currentHour < 10) { targetDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), firstPeriod[0], firstPeriod[1]); } else if (currentHour >= 10) { targetDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), secondPeriod[0], secondPeriod[1]); } else { // 0-2点时,目标为当天10点(补全原代码的逻辑空白) targetDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), firstPeriod[0], firstPeriod[1]); } break; default: // 周日、周一、周六 const firstHour = firstPeriod[0]; const firstMin = firstPeriod[1]; // 判断当前时间是否在第一个时段之前 if (currentHour < firstHour || (currentHour === firstHour && currentMinute < firstMin)) { targetDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), firstHour, firstMin); } else { targetDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), secondPeriod[0], secondPeriod[1]); } } // 计算时间差(秒) const nowTime = now.getTime(); const targetTime = targetDate.getTime(); let diff = Math.floor((targetTime - nowTime) / 1000); // 如果目标时间已过,自动切换到下周对应时间 if (diff <= 0) { diff += WEEK_SECONDS; } return diff; } /** * 启动倒计时器 * @param {number} totalSecs - 总倒计时秒数 */ function startTimer(totalSecs) { let remainingSecs = totalSecs; ticker = setInterval(tick, 1000); tick(); // 初始渲染倒计时 /** * 每秒更新倒计时 */ function tick() { if (remainingSecs > 0) { remainingSecs--; } else { clearInterval(ticker); // 倒计时结束后重新获取目标时间,启动新的倒计时 const newDiff = getTargetTimeDiff(); startTimer(newDiff); } // 拆分天、时、分、秒 const days = Math.floor(remainingSecs / DAY_SECONDS); remainingSecs %= DAY_SECONDS; const hours = Math.floor(remainingSecs / 3600); remainingSecs %= 3600; const mins = Math.floor(remainingSecs / 60); const secsLeft = remainingSecs % 60; // 这里替换成你的页面渲染逻辑,比如: // document.getElementById('countdown-display').textContent = `${days}天 ${hours}时 ${mins}分 ${secsLeft}秒`; } } // 页面加载时启动倒计时 const initialDiff = getTargetTimeDiff(); startTimer(initialDiff);
优化亮点
- 修复核心bug:每次计算都重新获取当前时间,修正了原代码中时间不更新的问题;补全了周三、周五0-2点的逻辑空白,修复了
getHours少括号的错误。 - 配置化管理:所有时段规则集中在
dayConfigs数组,以后修改时段只需修改数组,不用动逻辑代码,维护成本大幅降低。 - 代码结构清晰:拆分了时间计算和计时器逻辑,每个函数职责单一,可读性更强。
- 减少冗余:去掉了重复的Date创建和判断逻辑,代码量减少近一半。
内容的提问来源于stack exchange,提问作者Benjamin Dunham




