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

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 / function
    if (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); // 移除 null
  
      return `{${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 / number
    if (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 等),我可以继续写出一个完整版本,是否继续?

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GISer_Jinger

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值