[Javascript进阶]JSON.stringify与JSON.parse详解
JSON.stringfy
JSON.stringify 的核心作用是:
👉 将 JS 的对象、数组、基本类型转换为合法的 JSON 字符串。
手撕实现时,要考虑以下几个方面:
基本类型
处理:
- string → 加上双引号,注意
转义
;- number, boolean, null → 转为字符串;
- undefined, function, symbol →
在对象中会被忽略,在数组中会变成 null
。
数组
处理:
- 遍历每个元素,递归处理;
- 不能跳过 null 或 undefined,需转为 null。
对象
处理:
- 遍历 hasOwnProperty
- 排除 function、undefined、symbol 的值;
- 忽略不可序列化的 key。
replacer
(函数或数组)
- 如果 replacer 是一个函数,则对每个键值对调用它
- 如果 replacer 是一个数组,则只包含数组中指定的键
space
(缩进)
- 如果 space 是数字,则使用指定数量的空格进行缩进;
- 如果 space 是字符串,则使用该字符串进行缩进。
先通过
typeof 判断数据类型
,然后使用递归处理对象或数组结构
,基本类型直接转换为字符串
。
字符串需要加上双引号并处理转义
,数组中遇到 undefined / 函数需要转成 null
,对象中则会忽略这些键。
最后拼接出 JSON 字符串结构
,模拟实现原生的 JSON.stringify。
简单版本:
function myStringify(obj) {if (obj === null) return "null";if (typeof obj === "number" || typeof obj === "boolean") return String(obj);if (typeof obj === "string") return `"${obj}"`;if (Array.isArray(obj)) {const arr = obj.map(item => myStringify(item) ?? "null");return `[${arr.join(",")}]`;}if (typeof obj === "object") {const keys = Object.keys(obj);const props = keys.map(key => {const value = myStringify(obj[key]);if (value === undefined) return undefined;return `"${key}":${value}`;}).filter(Boolean);return `{${props.join(",")}}`;}// 其他类型(如 function、undefined、symbol)不能序列化return undefined;
}
复杂版本
function myJSONStringify(value, replacer, space) {// 处理 replacer 函数或数组if (typeof replacer === 'function') {value = replacer('', value); // 根节点 key 为 ""} else if (Array.isArray(replacer) && value && typeof value === 'object') {// 只保留 replacer 中存在的键value = Object.fromEntries(Object.entries(value).filter(([key]) => replacer.includes(key)));}// 处理 space 缩进参数const indentStr = typeof space === 'number' ? ' '.repeat(space) : (typeof space === 'string' ? space : '');const newline = indentStr ? '\n' : '';// 处理基本类型if (value === null) return 'null';if (typeof value === 'string') {// 字符串需要转义引号和反斜线return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;}if (typeof value === 'number' || typeof value === 'boolean') {return String(value);}// 不能序列化 undefined / symbol / functionif (typeof value === 'undefined' || typeof value === 'function' || typeof value === 'symbol') {return undefined; // JSON.stringify 会忽略这些值}// 递归处理数组if (Array.isArray(value)) {const elements = value.map(el => {const str = myJSONStringify(el, replacer, space);return str === undefined ? 'null' : str;});return `[${newline}${elements.map(e => indentStr + indentStr + e).join(',' + newline)}${newline}${indentStr+indentStr}]`;}// 递归处理对象if (typeof value === 'object') {const entries = Object.entries(value).map(([key, val]) => {const strVal = myJSONStringify(val, replacer, space);if (strVal === undefined) return null; // 跳过无法序列化的值return `${indentStr}${indentStr}"${key}":${indentStr ? ' ' : ''}${strVal}`;}).filter(Boolean); // 移除 nullreturn `{${newline}${entries.join(',' + newline)}${newline}${indentStr}}`;}// 非法类型抛出异常throw new TypeError('Converting circular structure to JSON');}const obj = {name: "Alice",age: 30,greet: function () {},married: false,skills: ["JS", "Vue"],nested: {city: "Paris"},undef: undefined};console.log(myJSONStringify(obj, null, 2));
JSON.parse()
JSON.parse 本质上是
一个把字符串格式的 JSON 数据解析为 JavaScript 对象
的过程。它会:
先校验字符串是否是合法的 JSON 格式;
然后逐层解析字符串结构;
再将其转换为数组、对象、字符串、数值等 JS 数据类型;
如果传了 reviver 函数,则对每一层属性递归调用进行加工处理。
当然,原生的 JSON.parse 内部是
基于词法分析器 +语法分析器
来实现的,我们手撕一个简化版本,支持对象、数组、字符串、数字、布尔值和null,不支持函数、symbol、undefined,也不考虑循环引用
。
function myParse(str) {// 去除头尾空格str = str.trim();// null / boolean / numberif (str === 'null') return null;if (str === 'true') return true;if (str === 'false') return false;if (!isNaN(str)) return Number(str);// 字符串if (str.startsWith('"') && str.endsWith('"')) {return str.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');}// 数组if (str.startsWith('[')) {const inner = str.slice(1, -1).trim();if (!inner) return [];return inner.split(',').map(s => myParse(s));}// 对象if (str.startsWith('{')) {const obj = {};const inner = str.slice(1, -1).trim();if (!inner) return obj;const pairs = inner.split(','); // 假设无嵌套for (let pair of pairs) {const [rawKey, rawVal] = pair.split(':');const key = myParse(rawKey);const val = myParse(rawVal);obj[key] = val;}return obj;}throw new Error('Invalid JSON format');}
JSON.parse 底层是通过“
词法分析 + 递归语法解析器
”实现的,属于编译器前端原理
的应用。
浏览器引擎(如 V8、SpiderMonkey)直接在 C++ 层构建解析器
,安全高效,不依赖 JS 的 eval
。
JSON.parse
的底层实现原理可以从两个角度来理解:语法解析原理(理论) 和 JavaScript 引擎实际实现(实践)。
✅ 一、JSON.parse 是做什么的?
它的任务是:
👉 将 JSON 字符串解析为 JavaScript 对象。
const obj = JSON.parse('{"name":"Alice","age":25}');
这涉及两个核心环节:
- 词法分析(将字符串分成有意义的“词法单元”)
- 语法分析(解析)(将词法单元转为 JS 对象结构)
🔧 二、底层实现原理(简化版)
1. 词法分析(Lexical Analysis)
把 JSON 字符串拆分为一系列 Token,例如:
{"name":"Alice","age":25}
被拆成这些 token:
{ → 左花括号
"name" → 字符串
: → 冒号
"Alice" → 字符串
, → 逗号
"age" → 字符串
: → 冒号
25 → 数字
} → 右花括号
2. 语法分析(Syntax Parsing)
按照 JSON 的语法规则,识别结构并转换为 JS 对象。
JSON 的语法定义大致是(符合 LL(1) 文法):
value ::= object | array | string | number | "true" | "false" | "null"
object ::= "{" [member ("," member)*] "}"
member ::= string ":" value
array ::= "[" [value ("," value)*] "]"
3. 递归下降解析器(Recursive Descent Parser)
大多数 JSON 解析器采用递归下降解析(递归地分析每种语法结构),伪代码如下:
function parseValue() {if (nextToken === '{') return parseObject();if (nextToken === '[') return parseArray();if (nextToken is string) return parseString();if (nextToken is number) return parseNumber();if (nextToken === "true"/"false"/"null") return literal;
}
4. 边解析边构建 JS 对象
一边解析结构,一边构建对象或数组,最终还原出 JS 的值。
⚙️ 三、真实引擎中的 JSON.parse 实现
🔥 V8 引擎(Chrome / Node.js)内部实现:
在 V8 源码中:
- 使用
JsonParser
类 - 代码位于:
src/json/json-parser.cc
- 它会把输入字符串转成 UTF-16,然后逐字符解析生成 AST → 对象
✅ 安全性上,V8 不使用
eval
,也不允许执行任意代码,仅限合法 JSON
⚠️ 四、与 eval 的区别?
比较点 | JSON.parse | eval() |
---|---|---|
安全性 | ✅ 安全,仅支持 JSON 语法 | ❌ 危险,能执行任意代码 |
性能 | ✅ 快,C++ 层解析 | ❌ 慢,走 JS 编译器 |
使用推荐 | 👍 推荐 | 🚫 避免 |
📚 五、自定义简化版 JSON.parse(手撕核心)
下面是一个简化版本(仅支持对象、字符串、数字):
function simpleParse(json) {if (json[0] === '{') {const obj = {};const body = json.slice(1, -1).trim(); // 去掉{}const pairs = body.split(','); // 拆键值对for (const pair of pairs) {const [key, val] = pair.split(':').map(s => s.trim().replace(/^"|"$/g, ''));obj[key] = isNaN(val) ? val : Number(val);}return obj;}
}
console.log(simpleParse('{"a":"1","b":2}')); // {a: '1', b: 2}
✅ 总结一句话:
JSON.parse
底层是通过“词法分析 + 递归语法解析器”实现的,属于编译器前端原理的应用。
浏览器引擎(如 V8、SpiderMonkey)直接在 C++ 层构建解析器,安全高效,不依赖 JS 的eval
。
如你想深入了解 V8 中 JsonParser
的源码结构,或者自己用 JS 实现完整的递归解析器(支持嵌套数组、对象、null、true/false 等),我可以继续写出一个完整版本,是否继续?