)
前端开发中序列化(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、前端常用序列化与反序列化方法的对比表格方法 / 场景序列化 (对象 → 传输/存储格式)反序列化 (传输/存储格式 → 对象)主要特点适用场景注意事项JSONJSON.stringify()JSON.parse()• 通用性强• 可读性好• 支持嵌套结构• API 数据传输• 本地存储• 配置文件• 不支持函数、undefined、Symbol、循环引用• 日期对象会转为字符串FormDatanew FormData()append()手动构建对象 /Object.fromEntries()•专用于表单数据• 支持文件上传• 文件上传• 复杂表单提交• 不支持嵌套结构除非手动处理• 不能直接转为 JSONURLSearchParamsnew URLSearchParams()Object.fromEntries()/get()• 生成 query string• 符合 URL 规范• GET 请求参数拼接• 表单 URL 编码提交• 不支持嵌套对象• 值默认转为字符串Blob / ArrayBufferBlob()/FileReaderFileReader/ 解码方法• 二进制数据处理• 文件处理• 图像/音视频数据• WebSocket 二进制传输• 需要理解编码方式• 通常配合 FileReader 或 Response 使用structuredClonestructuredClone()structuredClone()• 深度克隆• 支持更多数据类型• 深拷贝对象• 跨上下文数据传递• 不支持函数、DOM 节点• 较新的浏览器 API自定义序列化实现toJSON方法手动解析 / 构造函数• 完全可控• 满足特殊业务需求• 需要自定义序列化逻辑如日期格式化、字段过滤• 需要手动维护序列化/反序列化一致性MessagePack第三方库第三方库• 比 JSON 更小更快• 二进制格式• 高性能数据传输• 带宽敏感场景• 需要额外引入库• 可读性差XMLDOMParser/ 字符串模板DOMParser/XMLSerializer• 结构严谨• 支持命名空间• 遗留系统集成• 需要严格文档结构的场景• 冗余信息多• 解析相对复杂快速选择指南需求推荐方法普通 API 数据交换JSON表单提交含文件FormDataURL 查询参数URLSearchParams文件/二进制数据处理Blob / ArrayBuffer深度克隆对象含 Date、Map 等structuredClone高性能紧凑数据传输MessagePack第三方库旧系统 XML 接口DOMParser / XMLSerializer把数据流动的方向想象成内存对象 (Object) ⇄ 字符串 (String)。JSON.stringify()记忆口诀Obj → String逻辑你要把复杂的对象“压扁”成一行文字才能传输或存储。后缀含义-ify(动词后缀意为“使...变成”)。Stringify使变成字符串。类比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.stringify和JSON.parse时经常会遇到数据类型丢失的问题3.1 特殊类型丢失问题Date变成字符串Map/Set变成空对象{}undefined/Function/Symbol被忽略BigInt报错。解决方案使用自定义的replacer和reviver函数或使用第三方库。// 自定义序列化策略 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 指向 BB 指向 AJSON.stringify会直接抛出错误TypeError: Converting circular structure to JSON。解决方案使用flatted库轻量级兼容性好。使用structuredClone()(现代浏览器原生支持深度克隆且自动处理循环引用但主要用于克隆非严格意义的序列化字符串)。自定义 replacer 记录已访问节点。3.3 安全性风险 (原型污染)问题直接使用JSON.parse解析不可信的用户输入可能导致原型污染虽然比后端 Java/PHP 的反序列化漏洞轻微但仍需注意。解决方案永远不要信任前端接收到的来自用户直接输入的序列化数据。使用json-parse-better-errors等库提供更友好的错误提示。对于复杂场景考虑使用superstruct或zod在反序列化后进行数据校验。4. 现代替代方案与最佳实践随着前端应用复杂度的提升原生 JSON 已不能完全满足需求4.1structuredClone()(推荐用于深拷贝/传输)现代浏览器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. 工程化建议总结默认使用 JSON90% 的业务场景API 交互、配置存储使用JSON.stringify/parse即可简单且通用。处理特殊类型涉及Date、Map等类型时务必编写统一的transform工具函数不要在业务代码中散乱处理。大数据传输如果单次传输数据超过 100KB 且频率高考虑切换到MessagePack或Protobuf。Worker 通信利用postMessage的结构化克隆能力直接传递复杂对象无需手动 JSON 转换。安全防御后端接口应严格控制 Content-Type前端解析未知来源数据时需进行 Schema 校验使用 Zod/Yup。避免隐式转换在将数据存入localStorage前显式调用JSON.stringify取出时显式调用JSON.parse不要依赖框架的隐式行为。代码示例通用的安全序列化工具类// 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。