阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > JSON.stringify()改进

JSON.stringify()改进

作者:陈川 阅读数:40913人阅读 分类: JavaScript

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"}'

性能注意事项

虽然新特性增强了功能,但需要注意:

  1. 使用 replacer 函数会使序列化速度降低 30-50%
  2. 大数值的字符串转换会产生额外内存开销
  3. 深度嵌套对象的循环引用检测现在采用更严格的算法
// 性能对比测试
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

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌