JSON.parse 抛出的错误长这样:Unexpected token , in JSON at position 128。position 是从 0 开始的字符偏移,把它换算成行列就能直接跳到出错的位置:

function parseWithPos(text) {
  try {
    return JSON.parse(text);
  } catch (e) {
    const m = /position (\d+)/.exec(e.message);
    if (!m) throw e;
    const pos = Number(m[1]);
    const before = text.slice(0, pos);
    const line = before.split('\n').length;
    const col = pos - before.lastIndexOf('\n');
    throw new Error('第 ' + line + ' 行第 ' + col + ' 列:' + e.message);
  }
}

最常见的六种非法 JSON

  1. 尾随逗号:{"a":1,}。JavaScript 对象字面量允许,JSON 不允许。
  2. 单引号:{'a':1}。JSON 只认双引号,键也一样。
  3. 注释:JSON 标准里没有注释,接口返回带注释一定是手写的。
  4. NaN / Infinity / undefined:都不是合法 JSON 值。序列化前要转成 null 或字符串。
  5. BOM 头:文件开头多了 \uFEFF,表现为第一个字符就报错。读文件时先 strip 掉。
  6. 未转义的控制字符:字符串里混进真实的换行或制表符,必须写成 \n、\t。

还有一个更隐蔽的问题:大整数精度

JSON 里的数字会被转成双精度浮点,超过 2 的 53 次方就丢精度。典型场景是雪花算法生成的 19 位用户 ID:{"id": 1234567890123456789} 解析出来会变成 1234567890123456800,末尾几位直接变了。

解决办法是让服务端把这个字段序列化成字符串。这不是前端能补救的问题——精度在解析那一刻就丢了。

JSON.stringify 也会坑人

  • 值为 undefined、函数、Symbol 的键会被直接丢弃,数组里则变成 null。
  • 循环引用会抛 TypeError,需要自己写 replacer 或者用 WeakSet 记录。
  • Date 对象会自动调用 toJSON 变成 ISO 字符串,时区信息可能和你预期的不一样。
  • NaN 和 Infinity 会被转成 null,且不会报错——这是最容易被忽视的一条。
调试时把格式化后的结果和原文对照,比盯着一行压缩 JSON 找逗号快得多。用JSON 格式化把内容贴进去,出错位置会直接标出来。