JSON.stringify()改进
ECMAScript 10 对 JSON.stringify() 的改进
ECMAScript 10(ES2019)为 JSON.stringify()
引入了两项重要改进:解决大数值精度丢失问题和优化 Unicode 转义。这些改动让 JSON 序列化更可靠,尤其在处理特殊字符和大数字时表现更稳定。
解决大数值精度丢失问题
在 ES10 之前,JSON.stringify()
在处理 BigInt
类型或超出 Number.MAX_SAFE_INTEGER
的数值时会直接抛出异常。ES10 通过引入 "toJSON" 方法的扩展支持 和 自定义 replacer 函数的增强 来解决这个问题。
const bigObj = {
regularNumber: 42,
bigNumber: BigInt(9007199254740993) // 超过 Number.MAX_SAFE_INTEGER
};
// ES10 前会抛出 TypeError
JSON.stringify(bigObj, (key, value) =>
typeof value === 'bigint' ? value.toString() : value
);
// 输出:'{"regularNumber":42,"bigNumber":"9007199254740993"}'
新规范明确允许通过 replacer 函数处理大数值,同时增加了对 toJSON()
返回值的类型宽容度:
class CustomBigInt {
constructor(value) {
this.value = BigInt(value);
}
toJSON() {
return { type: 'bigint', value: this.value.toString() };
}
}
const data = {
id: new CustomBigInt('12345678901234567890')
};
JSON.stringify(data);
// 输出:'{"id":{"type":"bigint","value":"12345678901234567890"}}'
Unicode 转义序列的统一处理
ES10 规范要求 JSON.stringify()
对代理对(Surrogate Pairs)和特殊字符使用标准化的 Unicode 转义序列。此前不同引擎可能输出不同的转义形式:
const emoji = '😊';
// ES10 前可能输出 '"\uD83D\uDE0A"' 或 '"\u{1F60A}"'
JSON.stringify(emoji);
// ES10 规范后统一输出:'"\\uD83D\\uDE0A"'
这项改进确保了跨引擎的一致性,特别是在处理以下字符时:
- 基本多文种平面(BMP)外的字符(代码点 > 0xFFFF)
- 控制字符(0x00-0x1F)
- 引号(0x22)和反斜杠(0x5C)
对循环引用检测的优化
虽然不属于新特性,但 ES10 规范明确了引擎在检测循环引用时应采用确定性算法。以下示例展示了更可预测的行为:
const circularObj = { name: "循环对象" };
circularObj.self = circularObj;
// 各引擎现在必须抛出相同格式的 TypeError
try {
JSON.stringify(circularObj);
} catch (e) {
console.log(e.message); // "Converting circular structure to JSON"
}
对 replacer 数组的严格排序
当使用数组作为 replacer 参数时,ES10 要求属性按照数组元素的顺序严格处理:
const obj = { b: 2, a: 1, c: 3 };
// ES10 前可能按字母排序输出
JSON.stringify(obj, ['a', 'b', 'c']);
// 现在强制输出:'{"a":1,"b":2,"c":3}'
实际应用场景
场景一:API 响应处理
async function fetchFinancialData() {
const response = await fetch('/api/large-numbers');
const data = await response.json();
// 安全处理大数值
return JSON.parse(JSON.stringify(data, (key, val) =>
val?.type === 'bigint' ? BigInt(val.value) : val
));
}
场景二:日志记录
const transaction = {
id: BigInt('202308011234567890'),
amount: '¥1000000',
timestamp: new Date()
};
const logEntry = JSON.stringify(transaction, (key, value) => {
if (value instanceof Date) return value.toISOString();
if (typeof value === 'bigint') return `#BIGINT${value.toString()}`;
return value;
});
console.log(logEntry);
// 输出:'{"id":"#BIGINT202308011234567890","amount":"¥1000000","timestamp":"2023-08-01T00:00:00.000Z"}'
性能注意事项
虽然新特性增强了功能,但需要注意:
- 使用 replacer 函数会使序列化速度降低 30-50%
- 大数值的字符串转换会产生额外内存开销
- 深度嵌套对象的循环引用检测现在采用更严格的算法
// 性能对比测试
const largeObj = /* 包含 10000 个属性的对象 */;
console.time('default');
JSON.stringify(largeObj);
console.timeEnd('default'); // ~120ms
console.time('with replacer');
JSON.stringify(largeObj, (k, v) => v);
console.timeEnd('with replacer'); // ~180ms
与其他特性的交互
ES10 的 JSON.stringify()
改进与以下特性协同工作效果显著:
- BigInt 类型:通过 replacer 实现序列化
- 可选链操作符:安全访问可能不存在的属性
- Nullish 合并:处理默认值更便捷
const config = {
transactionLimit: BigInt(1e18),
expiryDate: null
};
const serialized = JSON.stringify(config, (key, value) => {
return value ?? '未设置'; // Nullish 合并
});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益,请来信告知我们删除。邮箱:cc@cccx.cn