json深拷贝失败的关键“特殊字符”包括u+2028/u+2029行/段落分隔符及控制字符(u0000–u001f),因其在json中属语法错误;此外undefined、函数、symbol、bigint、date、regexp、map、set和循环引用等不可序列化值也会导致静默丢失或报错。

JSON 方法不能安全用于包含特殊字符的字符串深拷贝,尤其当原始字符串含 undefined、函数、Symbol、BigInt、Date、RegExp、Map、Set、循环引用 或 **控制字符(如 u0000–u001F)** 时,JSON.stringify() 会静默丢弃、转换或报错。
哪些“特殊字符”会导致 JSON 拷贝失败
所谓“特殊字符”,不只是肉眼可见的 emoji 或中文,更关键的是 JavaScript 中无法被 JSON 格式原生表示的值:
-
不可序列化的值:
undefined、函数、Symbol('a')、BigInt(123n)——JSON.stringify()直接忽略或抛错 -
控制字符与非法 Unicode:如
"hellou0000world"(含空字节)会被保留,但某些环境解析时可能截断;"uFFFF"等代理对边界外的码点可能引发解析异常 -
带换行/制表符的字符串:虽然
JSON.stringify("a b c")能正确转义为"a\nb\tc",但若后续用非标准 JSON 解析器处理,可能误解析 -
正则、日期、Map/Set 等对象:直接 stringify 后得到空对象
{}或null,丢失原始语义
为什么看似“只是字符串”也会出问题
即使你只传入一个字符串,如果它来自用户输入、文件读取或后端响应,可能隐含不可见控制字符(如 U+2028 行分隔符、U+2029 段落分隔符),这些在 JS 字符串中合法,但在 JSON 字面量中是语法错误:
JSON.parse('"line1u2028line2"') → 报错,因为 u2028 在 JSON 中不允许作为未转义的字符出现
安全替代方案(不依赖 JSON)
针对需保留所有类型和字符的深拷贝,推荐以下方法:
-
structuredClone()(现代浏览器 & Node.js 17.0+):原生支持
Date、RegExp、Map、Set、Blob、TypedArray和循环引用,且保留所有 Unicode 字符(包括 U+2028/U+2029) - MessageChannel + postMessage(跨上下文兼容):利用结构化克隆算法,适合需要兼容旧环境的场景(需异步)
-
第三方库(如 lodash.cloneDeep):对常见特殊值有良好处理,但注意其对
Symbol、BigInt、undefined的默认行为(通常忽略或转为 null) -
手动递归拷贝(仅限可控简单结构):可自定义处理
undefined、function、控制字符等,但开发成本高、易漏边界情况
如果必须用 JSON,如何最小化风险
仅当确认数据纯为 JSON 友好类型(string/number/boolean/null/array/object,且 string 不含 U+2028/U+2029)时,才考虑 JSON 方案,并加一层防护:
- 预处理字符串:用
str.replace(/[u2028u2029]/g, '\u$&')替换危险分隔符 - 捕获 stringify 异常:
try { JSON.stringify(val) } catch(e) { /* fallback */ } - 避免对未知来源的任意对象直接深拷贝,优先约定数据契约(如只接受 plain object)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











