You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript日期异常:VanillaJS与Vue3同代码输出不一致

问题分析:JavaScript日期处理异常原因及解决办法

问题背景

已知JavaScript日期处理存在特殊性,曾考虑改用moment.js或通过PHP后端处理以避免新增依赖,但希望搞清楚当前遇到的问题:需要根据指定起始日期和月份数,生成每月递增1个月的日期数组,但使用某高赞addMonths函数时,实际每次会增加2个月;更奇怪的是,Vue3中循环内的console.log输出符合预期,但推入数组的结果却和VanillaJS一致出现异常。

复现代码

// 来源:Stack Overflow的addMonths函数
function addMonths(date, months) {
  let d = date.getDate();
  date.setMonth(date.getMonth() + +months);
  if (date.getDate() !== d) {
      date.setDate(0);
  }
  return date;
}

let starting_date = "2023-03-23";
let months_left = 6;
let header_months = [];
for (let i = 0; i < months_left + 1; i++) {
  let start_date = new Date(starting_date);
  console.log(addMonths(start_date, i));
  header_months.push(addMonths(start_date, i));
}

console.log(header_months);

不同环境输出

VanillaJS输出

0: Wed Mar 22 2023 18:00:00 GMT-0600 (Mountain Daylight Time)
1: Mon May 22 2023 18:00:00 GMT-0600 (Mountain Daylight Time)
2: Sat Jul 22 2023 18:00:00 GMT-0600 (Mountain Daylight Time)
3: Fri Sep 22 2023 18:00:00 GMT-0600 (Mountain Daylight Time)
4: Wed Nov 22 2023 18:00:00 GMT-0700 (Mountain Standard Time)
5: Mon Jan 22 2024 18:00:00 GMT-0700 (Mountain Standard Time)
6: Fri Mar 22 2024 18:00:00 GMT-0600 (Mountain Daylight Time)

Vue3输出

循环内console.log结果:

Show.vue:394 Wed Mar 22 2023 18:00:00 GMT-0600 (Mountain Daylight Time)
Show.vue:394 Sat Apr 22 2023 18:00:00 GMT-0600 (Mountain Daylight Time)
Show.vue:394 Mon May 22 2023 18:00:00 GMT-0600 (Mountain Daylight Time)
Show.vue:394 Thu Jun 22 2023 18:00:00 GMT-0600 (Mountain Daylight Time)
Show.vue:394 Sat Jul 22 2023 18:00:00 GMT-0600 (Mountain Daylight Time)
Show.vue:394 Tue Aug 22 2023 18:00:00 GMT-0600 (Mountain Daylight Time)

但最终header_months数组结果与VanillaJS完全一致,存在每月递增2个月的异常。

问题核心原因

  1. Date对象是引用类型:addMonths函数直接修改传入的Date对象本身,而非创建并返回新的Date对象。
  2. 重复修改同一对象:循环中对同一个start_date对象连续调用两次addMonths(start_date, i):
    • 第一次调用已将start_date增加了i个月
    • 第二次调用在已修改的start_date基础上再次增加i个月,最终实际增加了2*i个月
  3. Vue3 console.log的误导:循环内的console.log输出的是第一次调用后的正确结果,但推入数组的是第二次调用后的翻倍结果,导致视觉上出现“log正常、数组异常”的矛盾现象。

解决思路

方案1:修改addMonths函数,操作日期副本

修改函数,创建原日期的副本进行操作,避免修改传入的原Date对象:

function addMonths(date, months) {
  // 创建原日期的副本,隔离引用
  const newDate = new Date(date);
  const originalDate = newDate.getDate();
  
  newDate.setMonth(newDate.getMonth() + months);
  // 处理月末日期溢出(比如3月31日加1个月到4月30日)
  if (newDate.getDate() !== originalDate) {
    newDate.setDate(0);
  }
  
  return newDate;
}

方案2:循环内仅调用一次addMonths

将调用结果存入变量,再同时用于日志和数组推送,避免重复修改同一对象:

for (let i = 0; i < months_left + 1; i++) {
  let start_date = new Date(starting_date);
  const currentDate = addMonths(start_date, i);
  console.log(currentDate);
  header_months.push(currentDate);
}

方案3:使用更可靠的日期工具库

如果项目允许,可使用轻量的日期库(如date-fns)替代手写函数,避免原生Date API的坑:

import { addMonths } from 'date-fns';

// 示例用法
const startingDate = new Date("2023-03-23");
const headerMonths = Array.from({ length: 7 }, (_, i) => addMonths(startingDate, i));

内容的提问来源于stack exchange,提问作者Pawel K

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 11:35:38