core-js 中的 JSON.parse source text access:以 `JSON.rawJSON` 与 reviver 上下文实现大整数无损解析 📅 发布时间:2026/9/12 5:34:13 👁 浏览次数: core-js 中的 JSON.parse source text access以JSON.rawJSON与 reviver 上下文实现大整数无损解析【免费下载链接】core-jsStandard Library项目地址: https://gitcode.com/GitHub_Trending/co/core-js导读JSON.parse的 reviver 在解析过程中只能拿到解析后的 JS 值原始文本中的细节例如超出Number.MAX_SAFE_INTEGER的大整数、-0、尾数格式会被原生解析器提前丢弃。core-js 实现了 TC39 提案JSON.parse with sourceJSON.parsesource text access在 packages/core-js/modules/es.json.parse.js、es.json.raw-json.js、es.json.is-raw-json.js 与 es.json.stringify.js 中提供JSON.parse的context.source参数、JSON.rawJSON、JSON.isRawJSON以及配套的JSON.stringify补丁。读完本文你将掌握如何在大整数、高精度十进制等场景下借助 source text 与 raw JSON 实现无损往返并理解 core-js 内部自研 JSON 解析器与原始片段占位机制的工作原理。提案背景为什么需要源码文本访问原生JSON.parse(9007199254740993)会先把字面量转成 Number 再调用 reviver此时精度已经丢失——9007199254740993与9007199254740992在 IEEE 754 double 中不可区分。同样JSON.stringify也无法直接输出超出安全范围的整数字面量而BigInt又不被 JSON 语法支持。JSON.parse with source提案对应规范草案tc39.es/proposal-json-parse-with-source从两个方向解决该问题读方向JSON.parse的 reviver 增加第三个参数context其中context.source携带该值在原始文本中的精确片段写方向JSON.rawJSON(text)创建原始 JSON 值JSON.stringify遇到它时不重新序列化而是原样嵌入文本。这样大整数等精确数据就能在文本 → JS 值 → 文本的全链路中无损往返。API 签名一览core-js 文档docs/web/docs/features/proposals/json-parse-source-text-access.md给出的 TypeScript 签名为namespace JSON { isRawJSON(O: any): boolean; // patched for source support parse(text: string, reviver?: (this: any, key: string, value: any, context: { source?: string }) any): any; rawJSON(text: any): RawJSON; // patched for JSON.rawJSON support stringify(value: any, replacer?: Arraystring | number | (this: any, key: string, value: any) any, space?: string | number): string | void; }四个内置方法的职责分别是方法行为JSON.parse(text, reviver)在原生解析器不支持 source 时被整体替换为自研解析器reviver 第三参context.source提供原始文本片段JSON.rawJSON(text)校验text是合法的原始 JSON 值文本返回一个携带内部标记的 RawJSON 对象JSON.isRawJSON(O)判断一个值是否为JSON.rawJSON创建的 RawJSON 对象JSON.stringify(value, replacer, space)被补丁以支持 RawJSON 值的原样输出同时修复 Symbol 转换与畸形 Unicode 问题读方向JSON.parse的context.sourcecore-js 在 es.json.parse.js 中通过特性检测决定是否启用补丁var NO_SOURCE_SUPPORT fails(function () { var unsafeInt 9007199254740993; var source; nativeParse(unsafeInt, function (key, value, context) { source context.source; }); return source ! unsafeInt; });即如果原生JSON.parse的 reviver 无法拿到context.sourcecore-js 就用自研解析器整体替换JSON.parse。调用时如果未传 reviver 且底层JSON.parse行为正确PROPER_BASE_PARSE则直接走原生路径以保留性能parse: function parse(text, reviver) { return PROPER_BASE_PARSE !isCallable(reviver) ? nativeParse(text) : $parse(text, reviver); }当传入 reviver 时core-js 会用自己的解析器逐字符扫描文本并建立语法树节点Node对象记录value、end、source与子节点对象/数组走object()/array()分支数字走number()分支并通过slice(source, start, end)截取原始片段字符串走string()分支true/false/null走keyword()分支es.json.parse.js。随后internalize在递归调用 reviver 时只有未被修改且是原始值节点的值才会把context组装为{ source: node.source }var context unmodified typeof node.source string ? { source: node.source } : {};这一设计意味着context.source只对 JSON 原始值数字、字符串、布尔、null有意义对象与数组节点返回空的context。测试 tests/unit-global/es.json.parse.js 完整验证了这一行为parse(1234, spy); // source 1234 parse(1234, spy); // source 1234 parse(null, spy); // source null parse({}, spy); // source undefined parse([], spy); // source undefined parse(9007199254740993, spy);// source 9007199254740993写方向JSON.rawJSON与JSON.isRawJSONJSON.rawJSON的校验规则es.json.raw-json.js 的实现展示了它严格的入参约束rawJSON: function rawJSON(text) { var jsonString toString(text); if (jsonString || isWhitespace(at(jsonString, 0)) || isWhitespace(at(jsonString, jsonString.length - 1))) { throw new $SyntaxError(ERROR_MESSAGE); // Unacceptable as raw JSON } var parsed parse(jsonString); if (typeof parsed object parsed ! null) throw new $SyntaxError(ERROR_MESSAGE); var obj create(null); setInternalState(obj, { type: RawJSON }); createProperty(obj, rawJSON, jsonString); return FREEZING ? freeze(obj) : obj; }要点有三文本不能为空且首尾不允许出现空白字符、\t、\n、\r否则抛SyntaxError(Unacceptable as raw JSON)文本必须能被解析为 JSON 原始值数字、字符串、布尔、null对象与数组会被拒绝返回的对象以null为原型Object.create(null)通过内部状态槽{ type: RawJSON }打标并把原始文本存放在自身的rawJSON属性中在支持冻结的环境中该对象还会被Object.freeze冻结。对应单元测试tests/unit-global/es.json.raw-json.js验证了rawJSON(1).rawJSON 1、冻结行为以及三种非法输入的抛错assert.throws(() rawJSON(qwe), SyntaxError, invalid 1); assert.throws(() rawJSON({}), SyntaxError, invalid 2); assert.throws(() rawJSON(), SyntaxError, invalid 3);JSON.isRawJSON的内部状态判定es.json.is-raw-json.js 只是把 internals/is-raw-json.js 导出为静态方法。其判定完全基于内部状态槽而非外观module.exports function isRawJSON(O) { if (!isObject(O)) return false; var state getInternalState(O); return !!state state.type RawJSON; };因此伪造{ rawJSON: ... }的普通对象不会被误判为 RawJSON。特性检测与原生优先internals/native-raw-json.js 用于探测宿主环境是否已原生支持module.exports !fails(function () { var unsafeInt 9007199254740993; var raw JSON.rawJSON(unsafeInt); return !JSON.isRawJSON(raw) || JSON.stringify(raw) ! unsafeInt; });若环境原生可用es.json.raw-json.js与es.json.is-raw-json.js中的forced: !NATIVE_RAW_JSON使补丁跳过避免重复定义。JSON.stringify如何原样输出 RawJSONes.json.stringify.js 在原生stringify存在的前提下被补丁触发条件为WRONG_SYMBOLS_CONVERSION || ILL_FORMED_UNICODE || !NATIVE_RAW_JSON。其核心机制是占位标记替换生成一个唯一占位符RAW_MARK uid()es.json.stringify.js在 replacer 回调中凡是isRawJSONValue(value)的值原生支持时直接用原值否则用isRawJSON内部检测就把value.rawJSON压入rawStrings数组并以RAW_MARK 序号的字符串占位返回es.json.stringify.js序列化完成后对输出文本做一遍扫描遇到RAW_MARK前缀的字符串就用rawStrings中对应的原始文本替换es.json.stringify.js。由于占位符会先被当作普通字符串加引号序列化最后再整体替换所以rawJSON(9007199254740993)最终以无引号的数字字面量出现在输出中而非被转义或精度丢失。此外同一文件还顺带修复了两类原生实现问题Symbol 值序列化不一致WRONG_SYMBOLS_CONVERSION覆盖 Edge/WebKit/V8 的差异与畸形 UnicodeILL_FORMED_UNICODE将孤立代理项输出为\udxxx转义。后者的行为可在 tests/unit-global/es.json.stringify.js 的Well‑formed JSON.stringify用例中验证。实战示例大整数无损往返下面这个例子完整来自 core-js 文档json-parse-source-text-access.md演示读取与写入两条路径的组合用法function digitsToBigInt(key, val, { source }) { return /^\d$/.test(source) ? BigInt(source) : val; } function bigIntToRawJSON(key, val) { return typeof val bigint ? JSON.rawJSON(String(val)) : val; } const tooBigForNumber BigInt(Number.MAX_SAFE_INTEGER) 2n; JSON.parse(String(tooBigForNumber), digitsToBigInt) tooBigForNumber; // true const wayTooBig BigInt(1${ 0.repeat(1000) }); JSON.parse(String(wayTooBig), digitsToBigInt) wayTooBig; // true const embedded JSON.stringify({ tooBigForNumber }, bigIntToRawJSON); embedded {tooBigForNumber:9007199254740993}; // true理解这段代码的关键解析侧digitsToBigInt接收 reviver 第三参{ source }当source匹配/^\d$/纯数字文本时直接用BigInt(source)重建精确整数。因为source是原始文本而非已被舍入的 Number所以无论数字多大都能精确还原序列化侧bigIntToRawJSON把bigint值转换为JSON.rawJSON(String(val))stringify再把它原样嵌入 JSON 文本最终得到不带引号的大数字面量9007199254740993。如何接入与按需引入Entry points根据文档中的 Entry points 说明对应仓库 docs/web/docs/features/proposals/json-parse-source-text-access.md可按需引入core-js/proposals/json-parse-with-source core-js(-pure)/es|stable|actual|full/json/is-raw-json core-js(-pure)/es|stable|actual|full/json/parse core-js(-pure)/es|stable|actual|full/json/raw-json core-js(-pure)/es|stable|actual|full/json/stringify其中core-js/proposals/json-parse-with-source是聚合入口内部依次加载esnext.json.is-raw-json、esnext.json.parse、esnext.json.raw-json三个模块见 packages/core-js/proposals/json-parse-with-source.jscore-js与core-js-pure两个包均提供es标准、stable稳定特性、actual当前引擎实际缺失、full全部四档粒度便于按需打包除了parse/raw-json/is-raw-json还需要json/stringify入口才能获得 RawJSON 的原样输出能力。典型用法以 ESM 语法示意import core-js/proposals/json-parse-with-source; // 或按需引入 import core-js/actual/json/raw-json; import core-js/actual/json/is-raw-json; import core-js/actual/json/parse; import core-js/actual/json/stringify;也可以直接使用全量包import core-js;或core-js-pure的对应入口。esnext前缀的模块esnext.json.*对应提案阶段能力可单独 import。与标准 JSON 模块的关系core-js 没有为JSON提供整体 polyfillJSON对象仅在 IE7 及更早环境中缺失而是按当前标准修补已有实现见 docs/web/docs/features/ecmascript/json.md。parse、raw-json、is-raw-json、stringify四个模块共同构成 ECMAScript JSON 特性的一部分其中JSON.stringify的补丁还会顺带覆盖 well-formed stringify 与 Symbol 转换等历史引擎缺陷。若仅需标准层面的JSON.parse严格性修正例如负零与尾部空白处理可只引入es.json.parse相关入口。边界与注意事项context.source只对原始值有效对象与数组节点拿到的是空context{}这是由internalize中的typeof node.source string判断决定的JSON.rawJSON拒绝对象与数组传入{}、[]会抛SyntaxError空字符串、首尾空白文本同样被拒绝JSON.stringify(BigInt)仍然抛TypeError必须配合 replacer如bigIntToRawJSON先把 bigint 转成 RawJSONstringify本身不直接支持 bigintRawJSON 对象不可伪装JSON.isRawJSON依赖内部状态槽{ type: RawJSON }普通对象即使带有rawJSON属性也会返回false原生优先在已原生支持该提案的引擎上core-js 通过NATIVE_RAW_JSON与NO_SOURCE_SUPPORT特性检测跳过补丁避免重复打补丁。小结core-js 对JSON.parse with source提案的实现形成了完整的闭环自研的 es.json.parse.js 解析器负责在 reviver 中提供context.sourcees.json.raw-json.js 与 internals/is-raw-json.js 负责 RawJSON 值的创建与判定es.json.stringify.js 通过占位标记机制实现原样输出。配合 tests/unit-global/es.json.parse.js、tests/unit-global/es.json.raw-json.js 与 tests/unit-global/es.json.stringify.js 中的完整用例这套方案让大整数、高精度文本等精确 JSON场景在旧引擎上也能可靠工作。【免费下载链接】core-jsStandard Library项目地址: https://gitcode.com/GitHub_Trending/co/core-js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考