json.parse 的 reviver 函数对解析后每一层键值对逐个调用,先最深层后冒泡;需校验字符串是否为合法日期格式(如 iso 8601),避免误转非日期字符串或遗漏 return 导致属性被删。

JSON.parse 的 reviver 函数到底怎么触发
它不是全局钩子,而是对 JSON.parse 解析后**每一层的键值对逐个调用**:先调最深层,再往上冒泡。只要某个属性值是字符串、且符合日期格式(比如 "2023-10-05T14:30:00.000Z"),你就可以在 reviver 里判断并替换成 Date 实例。
注意:reviver 的 this 指向当前对象(父级),key 是字符串,value 是已解析的原始值(还没被替换)——所以你不能靠 typeof value === 'string' 就无脑转,得加格式校验。
常见错误现象:
– 把所有字符串都转成 Date,结果把 "id": "abc123" 变成 Invalid Date
– 忘了 return 值,导致该字段被删(reviver 返回 undefined 会删除该属性)
怎么写一个安全的日期 reviver
核心是识别 ISO 8601 格式字符串,同时兼容常见后端返回变体(如带空格的 "2023-10-05 14:30:00" 或带毫秒的 "2023-10-05T14:30:00.123")。不建议用正则硬匹配全集,简单有效的方式是:
- 先检查
typeof value === 'string' - 再用
new Date(value).toISOString()反向验证:如果能生成合法 ISO 字符串,且与原字符串“语义等价”,就可信 - 但要注意:
new Date("2023-10-05")和new Date("2023-10-05T00:00:00")生成的 ISO 不同(后者含时区),所以更稳妥的是用Date.parse(value) !== NaN+ 长度/字符特征初筛
实操建议用这个轻量版:
function dateReviver(key, value) {
if (typeof value === 'string') {
// 粗筛:长度在 10~30 之间,且含 - : T 或空格
if (value.length >= 10 && value.length JSON.parse(jsonStr, dateReviver);
为什么不能在 reviver 里处理嵌套对象或数组的边界情况
reviver 是深度优先遍历,value 已经是解析后的 JS 值。比如 {"created_at": "2023-10-05T14:30:00Z", "meta": {"updated": "2023-10-06T09:15:00Z"}},reviver 会先收到 meta.updated 的字符串,再收到 meta 对象(此时 value 已是 { updated: Date }),最后才收到顶层对象。
这意味着:
– 你无法在 reviver 中“提前知道”某个字符串是否属于日期字段(没有 schema)
– 如果后端字段名不固定(比如有时叫 date,有时叫 occurred_at),光靠值判断容易误伤
– 数组项如果是日期字符串,也会被单独传入 reviver,没问题;但如果你依赖字段名(如只处理 key 包含 time 的),就得结合 key 判断
性能影响很小,但别在 reviver 里做复杂计算或异步操作——它同步执行,阻塞整个解析。
遇到 Invalid Date 却没报错,怎么定位问题
因为 new Date(invalidString) 不抛错,只返回 Invalid Date(其 getTime() 是 NaN)。最容易被忽略的是时区和格式歧义:
-
"2023-10-05 14:30:00"在 Safari 中可能解析失败(缺少T),而 Chrome 宽松接受 -
"05/10/2023"是美式还是欧式?不同浏览器行为不一致 - 后端返回的
"2023-10-05T14:30:00+0800"(无冒号)在某些旧环境可能失败
调试技巧:
– 在 reviver 里加 console.log({ key, value, parsed: new Date(value) })
– 用 Date.parse(value) 替代构造函数,它只返回时间戳或 NaN,更干净
– 如果项目可控,让后端统一返回带 T 和 Z 的完整 ISO 8601(如 "2023-10-05T14:30:00.000Z"),这是最省心的方案











