[Javascript进阶]JSON.stringify与JSON.parse详解

article/2025/7/26 16:21:12

JSON.stringfy

JSON.stringify 的核心作用是:

👉 将 JS 的对象、数组、基本类型转换为合法的 JSON 字符串

手撕实现时,要考虑以下几个方面:

基本类型处理:

  1. string → 加上双引号,注意转义
  2. number, boolean, null → 转为字符串;
  3. undefined, function, symbol → 在对象中会被忽略,在数组中会变成 null

数组处理:

  1. 遍历每个元素,递归处理;
  2. 不能跳过 null 或 undefined,需转为 null。

对象处理:

  1. 遍历 hasOwnProperty
  2. 排除 function、undefined、symbol 的值;
  3. 忽略不可序列化的 key。

replacer(函数或数组)

  1. 如果 replacer 是一个函数,则对每个键值对调用它
  2. 如果 replacer 是一个数组,则只包含数组中指定的键

space(缩进)

  1. 如果 space 是数字,则使用指定数量的空格进行缩进;
  2. 如果 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 对象的过程。它会:

  1. 先校验字符串是否是合法的 JSON 格式;

  2. 然后逐层解析字符串结构;

  3. 再将其转换为数组、对象、字符串、数值等 JS 数据类型;

  4. 如果传了 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}');

这涉及两个核心环节:

  1. 词法分析(将字符串分成有意义的“词法单元”)
  2. 语法分析(解析)(将词法单元转为 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.parseeval()
安全性✅ 安全,仅支持 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 等),我可以继续写出一个完整版本,是否继续?

在这里插入图片描述


http://www.hkcw.cn/article/ivymXkVqwV.shtml

相关文章

VirtualBox给Rock Linux9.x配置网络

写这篇文章之前,先说明一下,我参考的是我之前写的《VirtualBox Linux网络配置》 我从CentOS7转到了Rock9,和配置Centos7一样,主流程没有变化,变化的是Rock9.x中的配置文件和使用的命令。 我再说一次,因为主…

逆向分析基础总结

一、了解计算机部件 CPU: 中央处理器。有三个重要的部件: 逻辑部件:负责算数运算,包括定点运算、浮点运算等。 寄存器部件:负责临时数据存储,一个CPU包含多个寄存器。 控制部件:负责发出指令所…

企业信息化集成方案:聚水潭·奇门数据对接金蝶云星空

聚水潭奇门数据集成到金蝶云星空:销售退货单更新方案 在企业信息化管理中,数据的高效流动和准确对接是实现业务流程自动化的关键。本文将分享一个实际案例,展示如何通过轻易云数据集成平台,将聚水潭奇门系统中的销售退货数据无缝…

多功能文档处理工具推荐

软件介绍 今天为大家介绍一款功能强大的文档编辑工具坤Tools,这是一款在吾爱论坛广受好评的办公软件。 软件背景 坤Tools是由吾爱论坛用户分享的软件,在论坛软件榜单上长期位居前列,获得了用户的一致好评。 软件性质 这是一款完全离线、…

软考-系统架构设计师-第十八章 面向服务架构设计理论与实践

面向服务架构设计理论与实践 18.1 SOA 的相关概念18.2 SOA 的发展历史18.3 SOA 的参考架构18.4 SOA 主要协议和规范18.5 SOA 设计的标准要求18.6 SOA 的作用与设计原则18.7 SOA 的设计模式18.8 构建 SOA 架构时应该注意的问题18.9 SOA 实施的过程 18.1 SOA 的相关概念 &#x…

AI书签管理工具开发全记录(五):后端服务搭建与API实现

文章目录 AI书签管理工具开发全记录(四):后端服务搭建与API实现前言 📝1. 后端框架选型 🛠️2. 项目结构优化 📁3. API路由设计 🧭分类管理书签管理 4. 数据模型定义 💾分类模型&…

CentOS-stream-9 Zabbix的安装与配置

一、Web环境搭建部署Zabbix时,选择合适的MariaDB、PHP和Nginx版本非常重要,以确保兼容性和最佳性能。以下是建议版本:Zabbix 6.4 MariaDB:官方文档推荐使用MariaDB 10.3或更高版本。对于CentOS Stream 9,建议使用Maria…

Scratch节日 | 龙舟比赛 | 端午节

端午节快乐! 这款专为孩子们打造的Scratch游戏——《龙舟比赛》,让你在掌控龙舟的竞速中,沉浸式体验中华传统节日的魅力! 🎮 游戏亮点 节日氛围浓厚:化身龙舟选手,在波涛汹涌的河流中展开刺激竞…

抢占先机!品牌如何利用软文营销领跑内容营销赛道?

在当今这个信息爆炸的时代,内容营销已成为品牌与消费者沟通的重要桥梁。而软文营销,作为内容营销的一种重要形式,更是以其独特的魅力和高效的传播力,成为众多品牌抢占市场先机的利器。本文将深入探讨品牌如何利用软文营销领跑内容…

用谷歌云服务器可以做些什么?

解锁云端无限可能 在当今数字化飞速发展的时代,云计算技术犹如一股强大的动力,推动着各行各业的变革与创新。而谷歌云服务器,作为云计算领域的佼佼者,正以其卓越的性能和丰富的功能,为全球用户开启一扇通往全新数字世界…

Jetson Orin Nano - SONY imx415 camera驱动开发

目录 前言: 调试准备工作: 修改内核默认打印等级 一、imx415驱动开发 1、硬件接线 2、设备树修改 2.1 创建 tegra234-p3767-camera-p3768-imx415-C-4lane.dtsi 文件 2.2 tegra234-p3767-camera-p3768-imx415-C-4lane.dtsi 添加到设备树 2.3 编译设备树 3、imx415驱动…

Linux ClearOS yum无法使用解决备忘

前述 一个长期未使用的系统ClearOS Linux系统,属于CentOS家族,该系统用于网络设备。现在继续使用时,发现通过yum命令无法更新或下载软件。在这里记录一下解决该问题的过程。 问题 SSL证书问题 问题描述 问题现象如下所示,执行…

Bonjour

Bonjour 是苹果的一套零配置网络协议,用于发现局域网内的其他设备并进行通信,比如发现打印机、手机、电视等。 一句话:发现局域网其他设备和让其他设备发现。 Bonjour 可以完成的工作 IP 获取名称解析搜索服务 实际应用场景示例&#xff0…

day16 数组的常见操作和形状

目录 Numpy数组基础知识 数组的维度 数组的秩 数组的简单创建 zeros创建数组 ones创建数组 arange创建数组 数组的随机化创建 数组的遍历 数组的运算 数组的索引 一维数组索引 二维数组索引 三维数组索引 SHAP值的深入理解 知识点: numpy数组的创建:简单…

利用 Synonyms 中文近义词库调优 RAG 服务,基于 Ollama, DeepSeek R1, Langchain

目录 比对代码对比结果Synonyms 中文近义词 EmbeddingsHuggingFaceEmbeddings GitHub https://github.com/hailiang-wang/llm-get-started/tree/master/003_rag_langchain 本文介绍,在基于 RAG 服务实现问答的过程中,使用两种 Embeddings 模式下&#…

HTML 文件反编译指南:优化与学习网页代码

原文:HTML 文件反编译指南:优化与学习网页代码 | w3cschool笔记 (请勿将文章标记为付费!!!) 一、何为 HTML 文件反编译? 反编译 HTML 文件即将其从可读代码转换为更精简的形式。实际上&#…

在 ODROID-H3+ 上安装 Win11 系统

在 ODROID-H3 上安装 Windows 11 系统。 以下是完整的步骤,包括 BIOS 设置、U 盘制作、安装和驱动处理,全程不保留之前的系统数据。 ✅ 准备工作 1. 准备一个 ≥8GB 的 USB 启动盘 用另一台电脑制作 Windows 11 安装盘。 👉 推荐工具&…

大话软工笔记—分离之业务与管理

1. 业务与管理的定义 业务,指企业为达成某个目标而进行的一系列活动(业务指的是“做事”)。 管理,为实现业务目标而进行的决策、计划、组织、指导、实施、控制的过程(管理是“管事”,“事”指的是业务&am…

DeepSeek R1 模型小版本升级,DeepSeek-R1-0528都更新了哪些新特性?

DeepSeek-R1‑0528 技术剖析:思维链再进化,推理性能飙升 目录 版本概览深度思考能力再升级基准测试成绩功能与体验更新API 变动与示例模型开源与下载结语 版本概览 DeepSeek 团队今日发布 DeepSeek‑R1‑0528 —— 基于 DeepSeek V3 Base(2…

请求分页中的内存分配

最小物理块数的确定 最小物理块数是确保进程能够正常运行所需的最少物理块数量。它是一个基础保障值,若分配的物理块数少于这个值,进程可能因无法完整加载必要页面而无法正常执行。例如,一个简单程序可能至少需要 3 个物理块来存放关键代码和…