先记住这个答案
new Date() 解析失败时不返回 null,也不抛错,而是返回一个内部时间值为 NaN 的 Date 对象,打印出来是 Invalid Date。它仍然是 Date 实例,所以 instanceof Date 判断不出来。正确的写法是 Number.isNaN(date.getTime()),因为 getTime() 返回内部毫秒数,无效时就是 NaN。另外要注意,new Date(2026, 1, 31) 这种不存在的日期不会变成 Invalid Date,而是被进位成 3 月 3 日,校验用户输入时要单独处理。
- Invalid Date 也是 Date 实例,时间值是
NaN - 检测写法:
Number.isNaN(date.getTime()) - 对无效日期调
toISOString()会抛RangeError - 2 月 31 日不是无效日期,会被进位成 3 月
Invalid Date 长什么样
先看一个有效日期和一个无效日期在各种检查下的表现。
const valid = new Date('2026-09-10T00:00:00Z')
const invalid = new Date('not-a-date')
const isValidDate = (value) =>
value instanceof Date && !Number.isNaN(value.getTime())
console.log(isValidDate(valid), isValidDate(invalid)) // true false
console.log(invalid instanceof Date, invalid.getTime()) // 仍是 Date,时间值是 NaN
console.log(JSON.stringify({ at: invalid })) // toJSON 返回 null
try {
invalid.toISOString()
} catch (err) {
console.log(err.name) // 这里才真正抛错
}查看输出与解释
true false
true NaN
{"at":null}
RangeErrorinstanceof 为 true,因为对象确实是用 Date 构造出来的,原型链没问题。坏掉的是它里面存的那个数。所以判断要分两步:先确认是 Date,再确认时间值不是 NaN。
注意 JSON.stringify 那一行。无效日期的 toJSON() 返回 null,不会抛错,结果请求体里悄悄多了一个 null 字段。后端收到时根本分不清是前端没填,还是日期解析坏了。
- 为什么不用全局
isNaN(date) isNaN(date)通常也能得到正确结果,因为它会先把 Date 转成数字,转换时调用的就是valueOf()。问题在于全局isNaN什么都先转数字,传一个普通字符串'hello'也返回true,意图不清楚。Number.isNaN(date.getTime())明确说的是「这个 Date 的时间值是不是NaN」,读代码的人一眼就懂。
2 月 31 日为什么检测不出来
表单里用户选了 2026 年 2 月 31 日,new Date(2026, 1, 31) 构造出来是一个完全有效的日期,只不过是 3 月 3 日。Date 对越界的字段会自动进位,所以 getTime() 不是 NaN,前面的检查拦不住。
function isRealDate(year, month, day) {
const date = new Date(year, month - 1, day) // 用户输入的月份是 1~12
return (
date.getFullYear() === year &&
date.getMonth() === month - 1 &&
date.getDate() === day
)
}
console.log(isRealDate(2026, 2, 28)) // 正常日期
console.log(isRealDate(2026, 2, 31)) // 被进位成 3 月 3 日
console.log(isRealDate(2024, 2, 29)) // 闰年有 29 日查看输出与解释
true
false
true思路很简单:日期如果被进位了,读回来的月份或日就会和输入对不上。这个函数同时也挡住了 NaN 输入,因为无效 Date 的 getFullYear() 返回 NaN,和任何数字都不相等。
容易答错的地方
- 只写
date instanceof Date - Invalid Date 也能通过这个检查。它只能说明对象类型对,说明不了时间值有效,必须再检查
getTime()。 - 用字符串
'Invalid Date'做判断 String(date) === 'Invalid Date'能用,但它依赖的是给人看的展示文本,不是可靠的判断依据。直接检查数值更干净,也不用先转一次字符串。- 以为
new Date()解析失败会抛错 - 大部分语言的日期解析失败会抛异常,JavaScript 的 Date 不会。它把错误藏在对象里,等到
toISOString()或者Intl.DateTimeFormat格式化时才抛RangeError。所以要在拿到 Date 的第一时间检查,别等到序列化环节。
面试官还会怎么问?
Date.parse 能用来检查吗?
能,Date.parse(str) 返回时间戳,解析失败返回 NaN,适合检查字符串。已经有 Date 对象时直接用 getTime() 就行,不用再转回字符串解析一遍。
跨 iframe 传过来的 Date 用 instanceof 会怎样?
每个 iframe 有自己的一套全局对象,父页面的 Date 和子页面的 Date 不是同一个构造函数,instanceof 会返回 false。可以改用 Object.prototype.toString.call(value) === '[object Date]' 判断类型,再检查 getTime()。
两位数的年份会有什么问题?
多参数构造时,0 到 99 的年份会被当成 1900 年加上这个数,new Date(99, 0, 1) 是 1999 年。要构造公元 99 年这种日期,得先建好 Date 再调用 setFullYear(99)。日常业务碰不到,但用户在年份输入框只敲了两位数时就会出现。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。