json深拷贝将nan转为null是因json标准不支持nan等特殊值;可通过预扫描定位替换位置,或用字符串占位法临时保真;长期应改用lodash.clonedeep等支持特殊值的方案。

JSON 方法深拷贝(JSON.parse(JSON.stringify(obj)))把 NaN 静默转成 null,不是 bug,是 JSON 标准的硬性限制——它根本不允许 NaN 作为合法值存在。这种“看起来没报错、实际丢了信息”的行为,最容易在数值校验、异常标记、算法边界判断中埋下隐患。
为什么 NaN 一定会变成 null
JSON 规范只定义了 null、数字、字符串、布尔、数组、对象六种类型,NaN 不在其中。当 JSON.stringify() 遇到它时,不会报错,也不会保留字符串 "NaN",而是按规范强制替换为 null。同理,Infinity 和 -Infinity 也一并归零。
NaN → nullInfinity → null-Infinity → null
怎么提前发现哪些地方被悄悄替换了
别等出问题才查。在调用深拷贝前,对原始对象做一次轻量扫描,记录所有 NaN、Infinity 出现的位置和类型:
DMap GL地图JSAPI(dmapgl)开发指南。用于编写、审查、调试 DMap 地图 API 代码,涵盖地图初始化、覆盖物渲染、图层管理、事件处理、控件交互等任务。当用户提及 dmap、dmapgl、jsapi‑gl 或相关地图开发需求时自动触发。
- 用
Object.keys()+ 递归遍历所有自有可枚举属性 - 对每个值分别用
Number.isNaN(val)、val === Infinity、val === -Infinity判断 - 记录路径(如
"data.metrics.score")和原始类型,方便后续比对
这样拷贝后只要检查对应路径是否变成了 null,就能立刻定位退化点。
不改 JSON 方案,也能绕过这个问题
如果暂时不能换库或写递归,可用字符串占位法临时保真:
- 深拷贝前,把所有
NaN替换成"__NaN__",Infinity换成"__Infinity__",-Infinity换成"__NegInfinity__" - 执行
JSON.parse(JSON.stringify(obj)) - 拷贝完成后,再把字符串占位符还原为对应特殊值(注意用
Object.is()或严格相等判断,避免误替换普通字符串)
更稳妥的长期解法
依赖 NaN 或 Infinity 语义的业务,不应把 JSON 方法当深拷贝主力:
- 用
lodash.cloneDeep:自动识别并保留这些值,还兼容Date、RegExp、循环引用等 - 自定义递归拷贝:手动处理
Number.isNaN、isFinite分支,控制每个类型的复制逻辑 - 服务端配合:若数据要跨端传输,可在序列化前统一约定特殊值编码规则(如用
{"type": "nan"}对象代替原始值)










