前端序列化和反序列化总结(JSON.stringify 和 JSON.parse 的局限,自定义通用的安全序列化工具类)

前端开发中,序列化(Serialization)和反序列化(Deserialization)是数据交互的关键环节。


JSON.stringify()和JSON.parse()是最常用的方法,但存在数据类型丢失(Date变为字符串)、循环引用报错等问题。


解决方案包括使用自定义replacer/reviver函数、structuredClone()处理复杂对象,以及第三方库处理二进制数据。


针对不同场景:

  • API交互推荐JSON
  • 表单提交用FormData
  • 高性能传输考虑MessagePack

工程实践中需注意特殊类型处理、安全防御和显式转换。


通过规范化处理可避免数据格式导致的bug。


表单相关:序列化、状态持久化、复杂数据处理(附:Object.fromEntries()方法 函数式对象 解释)


web表单提交和表单序列化的多种方式总结


Worker 对象 与 DedicatedWorkerGlobalScope 实例对比(附:序列化、结构化克隆算法、循环引用 解释)


前端序列化和反序列化总结


在前端开发中,序列化(Serialization) 和**反序列化(Deserialization)**是数据交互的核心环节,主要用于数据在网络传输、本地存储(如 localStorage/IndexedDB)以及状态管理中的格式转换。


1、核心概念

  • 序列化 (Serialization)
    • 定义 :将 JavaScript 对象、数组等复杂数据结构转换为字符串(通常是 JSON 格式)或二进制流的过程。
    • 目的:以便通过网络传输(HTTP 请求体)、持久化存储(存入 localStorage/数据库)或在不同进程间传递。
    • 前端典型操作JSON.stringify()
  • 反序列化 (Deserialization)
    • 定义:将字符串(如 JSON 字符串)或二进制流还原为内存中的 JavaScript 对象或数据结构的过程。
    • 目的:解析后端返回的数据、读取本地存储的数据,使其在代码中可操作。
    • 前端典型操作JSON.parse()

2、前端常用序列化与反序列化方法的对比表格:

方法 / 场景 序列化 (对象 → 传输/存储格式) 反序列化 (传输/存储格式 → 对象) 主要特点 适用场景 注意事项
JSON JSON.stringify() JSON.parse() • 通用性强 • 可读性好 • 支持嵌套结构 • API 数据传输 • 本地存储 • 配置文件 • 不支持函数、undefined、Symbol、循环引用 • 日期对象会转为字符串
FormData new FormData() + append() 手动构建对象 / Object.fromEntries() • 专用于表单数据 • 支持文件上传 • 文件上传 • 复杂表单提交 • 不支持嵌套结构(除非手动处理) • 不能直接转为 JSON
URLSearchParams new URLSearchParams() Object.fromEntries() / get() • 生成 query string • 符合 URL 规范 • GET 请求参数拼接 • 表单 URL 编码提交 • 不支持嵌套对象 • 值默认转为字符串
Blob / ArrayBuffer Blob() / FileReader FileReader / 解码方法 • 二进制数据处理 • 文件处理 • 图像/音视频数据 • WebSocket 二进制传输 • 需要理解编码方式 • 通常配合 FileReader 或 Response 使用
structuredClone structuredClone() structuredClone() • 深度克隆 • 支持更多数据类型 • 深拷贝对象 • 跨上下文数据传递 • 不支持函数、DOM 节点 • 较新的浏览器 API
自定义序列化 实现 toJSON 方法 手动解析 / 构造函数 • 完全可控 • 满足特殊业务需求 • 需要自定义序列化逻辑(如日期格式化、字段过滤) • 需要手动维护序列化/反序列化一致性
MessagePack 第三方库 第三方库 • 比 JSON 更小更快 • 二进制格式 • 高性能数据传输 • 带宽敏感场景 • 需要额外引入库 • 可读性差
XML DOMParser / 字符串模板 DOMParser / XMLSerializer • 结构严谨 • 支持命名空间 • 遗留系统集成 • 需要严格文档结构的场景 • 冗余信息多 • 解析相对复杂

快速选择指南

需求 推荐方法
普通 API 数据交换 JSON
表单提交(含文件) FormData
URL 查询参数 URLSearchParams
文件/二进制数据处理 Blob / ArrayBuffer
深度克隆对象(含 Date、Map 等) structuredClone
高性能紧凑数据传输 MessagePack(第三方库)
旧系统 XML 接口 DOMParser / XMLSerializer

把数据流动的方向想象成 内存对象 (Object) ⇄ 字符串 (String)


  • JSON.stringify()

    • 记忆口诀Obj → String
    • 逻辑:你要把复杂的对象"压扁"成一行文字才能传输或存储。
    • 后缀含义-ify (动词后缀,意为"使...变成")。
      • String + ify = 使变成字符串
      • 类比:Simplifying (简化), Terrifying (使恐怖)。
    • 场景 :存本地 (localStorage)、发请求 (body)。
  • JSON.parse()

    • 记忆口诀String → Obj
    • 逻辑 :你拿到一串文字,需要把它"解析/拆开"还原成可用的对象。
    • 单词含义Parse (解析)。
      • 就像语法分析一样,把句子拆解成结构。
    • 场景 :读本地、收响应 (response.json())。

一句话总结
想变字符串用 -ify (Stringify),想还原对象用 Parse

快速自测表

你的目标 此时数据形态 目标形态 应该用哪个? 记忆线索
存入 localStorage 对象 {} 字符串 "" JSON.stringify() -ify (变成字符串)
发送 POST 请求 对象 {} 字符串 "" JSON.stringify() 打包 寄出
接收 API 响应 字符串 "" 对象 {} JSON.parse() Parse (解析还原)
读取 localStorage 字符串 "" 对象 {} JSON.parse() 拆包 使用
打印日志调试 对象 {} 字符串 "" JSON.stringify() 为了看清结构转成文本

3、前端序列化的常见"坑"与解决方案

在使用原生的 JSON.stringifyJSON.parse 时,经常会遇到数据类型丢失的问题:


3.1 特殊类型丢失

  • 问题Date 变成字符串,Map/Set 变成空对象 {}undefined/Function/Symbol 被忽略,BigInt 报错。
  • 解决方案 :使用自定义的 replacerreviver 函数,或使用第三方库。
javascript 复制代码
// 自定义序列化策略
const customStringify = (v) => JSON.stringify(v, (key, value) => {
  if (value instanceof Map) {
    return { __type: 'Map', value: [...value] };
  }
  if (value instanceof Date) {
    return { __type: 'Date', value: value.toISOString() };
  }
  if (typeof value === 'bigint') {
    return { __type: 'BigInt', value: value.toString() };
  }
  return value;
});

// 自定义反序列化策略
const customParse = (s) => JSON.parse(s, (key, value) => {
  if (value?.__type === 'Map') {
    return new Map(value.value);
  }
  if (value?.__type === 'Date') {
    return new Date(value.value);
  }
  if (value?.__type === 'BigInt') {
    return BigInt(value.value);
  }
  return value;
});

3.2 循环引用 (Circular Reference)

  • 问题 :如果对象内部存在循环引用(A 指向 B,B 指向 A),JSON.stringify 会直接抛出错误 TypeError: Converting circular structure to JSON
  • 解决方案
    1. 使用 flatted 库(轻量级,兼容性好)。
    2. 使用 structuredClone() (现代浏览器原生支持,深度克隆且自动处理循环引用,但主要用于克隆,非严格意义的序列化字符串)。
    3. 自定义 replacer 记录已访问节点。

3.3 安全性风险 (原型污染)

  • 问题 :直接使用 JSON.parse 解析不可信的用户输入可能导致原型污染(虽然比后端 Java/PHP 的反序列化漏洞轻微,但仍需注意)。
  • 解决方案
    • 永远不要信任前端接收到的来自用户直接输入的序列化数据。
    • 使用 json-parse-better-errors 等库提供更友好的错误提示。
    • 对于复杂场景,考虑使用 superstructzod 在反序列化后进行数据校验。

4. 现代替代方案与最佳实践

随着前端应用复杂度的提升,原生 JSON 已不能完全满足需求:


4.1 structuredClone() (推荐用于深拷贝/传输)

现代浏览器(2026年已完全普及)支持的结构化克隆算法。

  • 优势 :原生支持 Date, Map, Set, RegExp, Blob, ImageBitmap 等,自动处理循环引用。
  • 局限 :它返回的是对象副本,不是字符串。若需存储或传输,仍需配合 postMessage (自动序列化) 或特定编码。
  • 场景 :Web Worker 通信 (postMessage 底层即使用此算法)、深度克隆状态。

4.2 二进制序列化协议 (高性能场景)

对于数据量大、对带宽敏感的场景(如即时游戏、高频交易看板):

  • Protocol Buffers (Protobuf) : Google 出品,强类型,体积极小。需 .proto 文件定义结构。
  • MessagePack: 类似 JSON 的二进制格式,无需预定义结构,兼容性好。
  • FlatBuffers: 零拷贝解析,速度极快,适合超大数据集。

4.3 状态管理中的序列化

在 Redux, Pinia, Vuex 中:

  • 持久化插件 (如 redux-persist, pinia-plugin-persistedstate):自动将 State 序列化存入 localStorage
  • 注意:需配置白名单,避免将不可序列化的对象(如包含 DOM 节点或复杂类实例的状态)存入存储,否则重启应用时会崩溃。

5. 工程化建议总结

  1. 默认使用 JSON :90% 的业务场景(API 交互、配置存储)使用 JSON.stringify/parse 即可,简单且通用。
  2. 处理特殊类型 :涉及 DateMap 等类型时,务必编写统一的 transform 工具函数,不要在业务代码中散乱处理。
  3. 大数据传输 :如果单次传输数据超过 100KB 且频率高,考虑切换到 MessagePackProtobuf
  4. Worker 通信 :利用 postMessage 的结构化克隆能力,直接传递复杂对象,无需手动 JSON 转换。
  5. 安全防御:后端接口应严格控制 Content-Type,前端解析未知来源数据时需进行 Schema 校验(使用 Zod/Yup)。
  6. 避免隐式转换 :在将数据存入 localStorage 前,显式调用 JSON.stringify,取出时显式调用 JSON.parse,不要依赖框架的隐式行为。

代码示例:通用的安全序列化工具类

TypeScript 复制代码
// utils/serializer.ts

export const SafeSerializer = {
  stringify: (obj: any): string => {
    return JSON.stringify(obj, (key, value) => {
      // 处理特殊类型
      if (value instanceof Date) return { _type: 'date', val: value.toISOString() };
      if (value instanceof Map) return { _type: 'map', val: Array.from(value.entries()) };
      if (value instanceof Set) return { _type: 'set', val: Array.from(value) };
      if (typeof value === 'bigint') return { _type: 'bigint', val: value.toString() };
      
      // 过滤不可序列化的类型
      if (typeof value === 'function' || typeof value === 'symbol' || value === undefined) {
        return undefined; 
      }
      
      return value;
    });
  },

  parse: (str: string): any => {
    try {
      return JSON.parse(str, (key, value) => {
        if (value?._type === 'date') return new Date(value.val);
        if (value?._type === 'map') return new Map(value.val);
        if (value?._type === 'set') return new Set(value.val);
        if (value?._type === 'bigint') return BigInt(value.val);
        return value;
      });
    } catch (e) {
      console.error('反序列化失败:', e);
      return null;
    }
  }
};

通过规范化的序列化处理,可以有效避免前端开发中因数据格式问题导致的难以排查的 Bug。

相关推荐
总有刁民想爱朕ha2 小时前
数据库行统计和字典导出工具Web版
前端·数据库
大雷神2 小时前
HarmonyOS APP<玩转React>开源教程二十二:每日一题功能
前端·react.js·开源·harmonyos
还是大剑师兰特2 小时前
Vue3 + Element Plus 日期选择器:开始 / 结束时间,结束时间不超过今天
前端·javascript·vue.js
Robbie丨Yang2 小时前
前端工程构建优化实践指南
前端
Saga Two2 小时前
Vue实现核心原理
前端·javascript·vue.js
PyHaVolask2 小时前
Web 技术核心术语
前端·http·web
殷忆枫2 小时前
基于STM32的ML307R连接Onenet平台
服务器·前端·javascript
Predestination王瀞潞2 小时前
6.5.3 软件->W3C HTML5、CSS3标准(W3C Recommendation):Selector网页选择器
前端·css3·html5
Java 码农2 小时前
vue cli 环境搭建
前端·javascript·vue.js