JSON.stringify 会丢失容器类型元信息,所有非原生 JSON 类型(如 Map、Set、Date、RegExp)均被扁平化为 object 或 array,原始语义和行为特征完全丧失。

JSON.stringify 本身不提供容器类型元信息,所有对象、数组、Map、Set、Date、RegExp 等,在序列化后都“扁平”为 JSON 原生结构(object 或 array),原始容器身份完全丢失。识别这种“自描述性缺失”,关键不是看输出字符串长什么样,而是**比对输入与输出的类型语义断层**。
看输出是否保留容器本质特征
原生 JSON 只有 object 和 array 两种复合类型,其他容器必须靠内容或约定“伪装”:
-
Array:能保留 → 输出是
[...],且元素顺序、稀疏性(如[1,,3])在解析后仍为数组 -
Plain Object:能保留 → 输出是
{...},但无法区分是{}、Object.create(null)还是 class 实例(原型和构造器全丢) -
Map / Set:彻底丢失容器身份 → 直接被忽略(Map 不可枚举键)、或转为空对象
{}、或若手动遍历转成数组则失去 Map/Set 的语义(如键类型不限、重复键覆盖等) -
Date:类型丢失 → 变成 ISO 字符串
"2026-05-09T06:26:00.000Z",parse 后是 string,不再是 Date 实例 -
RegExp:类型丢失 → 变成空对象
{},原始正则表达式源码、标志(g/i/m)全部消失 -
TypedArray(如 Uint8Array):内容可能保留,但类型丢失 → 默认转为普通数组
[1,2,3],无法还原 buffer、byteOffset、length 等特性
检查 key-value 层级是否发生静默坍缩
某些容器在 stringify 时会因不可枚举性、Symbol 键或 toJSON 行为导致字段“凭空消失”或“意外变形”:
- 用
Object.defineProperty(obj, 'hidden', { value: 42, enumerable: false })定义的属性 → 不会出现于输出中 - 以
Symbol('id')为键的属性 → 被完全忽略(Symbol 键不参与 JSON 序列化) - 对象含
toJSON()方法 → 输出的是该方法返回值,而非对象自身结构(例如new Date().toJSON()返回字符串,掩盖了它是 Date 的事实) - Map 转成对象再 stringify(如
Object.fromEntries(map))→ 若 key 是对象或 symbol,会变成"[object Object]"或直接丢弃,造成数据错位
验证能否无损 round-trip 还原容器
这是最实用的识别方式:把原始容器 stringify 再 parse,看能不能恢复成相同行为的实例:
const m = new Map([['a', 1]]); const s = JSON.stringify(m); const p = JSON.parse(s); p instanceof Map → falseconst d = new Date(); const s = JSON.stringify(d); const p = JSON.parse(s); p instanceof Date → false;typeof p === 'string' → trueconst r = /abc/g; const s = JSON.stringify({r}); // {"r":{}};parse 后 <code>r是空对象,不是 RegExp- 若你依赖
map.has(key)或set.size等 API → stringify+parse 后这些能力全部失效,说明自描述性已断裂
留意控制台调试时的“假象”误导
浏览器开发者工具常对对象做美化展示(比如把 Map 显示为 Map(1) {"a" => 1}),但这只是 UI 渲染,不代表它能被 JSON.stringify 正确捕获。真正检验方式只有执行 JSON.stringify(yourValue) 并观察返回字符串内容 —— 如果里面没有明确标识容器类型的字段(如 "$type": "Map")、也没有保留其关键行为特征(如 Map 的键值对结构、Set 的唯一性表达),那就确认发生了自描述性缺失。


















