JSON.stringify()(长文讲解)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观

前言

在 JavaScript 开发中,数据的格式转换是一个高频需求。无论是与后端 API 交互,还是在浏览器与服务器之间传输数据,开发者常常需要将 JavaScript 对象转换为字符串格式。此时,JSON.stringify() 就扮演了至关重要的角色。它不仅是开发者工具箱中的核心工具,更是理解数据序列化概念的入门钥匙。本文将从基础到进阶,结合案例与代码示例,深入解析 JSON.stringify() 的原理、用法及常见问题。


一、什么是 JSON.stringify()?

1.1 序列化与反序列化的概念

在编程中,序列化(Serialization)是指将数据结构或对象转换为可以存储或传输的格式,例如字符串或二进制数据;而反序列化(Deserialization)则是将序列化后的数据还原为原始对象。
JSON.stringify() 的作用,正是将 JavaScript 对象(Object)序列化为符合 JSON 标准的字符串。例如:

const user = { name: "Alice", age: 25 };  
const serializedUser = JSON.stringify(user);  
console.log(serializedUser); // 输出:{"name":"Alice","age":25}  

通过这一过程,原本复杂的对象可以被转化为可在网络中高效传输的纯文本数据。

1.2 与 JSON.parse() 的对比

JSON.stringify() 的“逆操作”是 JSON.parse(),后者将 JSON 字符串解析为 JavaScript 对象。两者的搭配使用,构成了数据序列化与反序列化的完整流程:

const jsonStr = '{"city":"Shanghai", "population":24150000}';  
const cityData = JSON.parse(jsonStr);  
console.log(cityData.city); // 输出:Shanghai  

理解这对函数的协作关系,是掌握数据转换的关键。


二、JSON.stringify() 的基础用法

2.1 基础语法与简单示例

JSON.stringify() 的基本语法为:

JSON.stringify(value, replacer?, space?);  

其中:

  • value:必选参数,表示要序列化的 JavaScript 对象、数组或值。
  • replacer:可选参数,用于过滤或转换序列化后的数据。
  • space:可选参数,控制缩进和空格,使输出更易读。

示例 1:序列化对象

const person = {  
  name: "Bob",  
  hobbies: ["reading", "coding"],  
  isDeveloper: true  
};  
const jsonPerson = JSON.stringify(person);  
console.log(jsonPerson);  
// 输出:{"name":"Bob","hobbies":["reading","coding"],"isDeveloper":true}  

示例 2:序列化嵌套对象与数组

const team = {  
  leader: "Charlie",  
  members: [  
    { name: "David", role: "Designer" },  
    { name: "Eve", role: "Engineer" }  
  ]  
};  
console.log(JSON.stringify(team));  
// 输出:{"leader":"Charlie","members":[{"name":"David","role":"Designer"},{"name":"Eve","role":"Engineer"}]}  

2.2 支持的数据类型与限制

JSON.stringify() 可序列化的数据类型包括:

  • 布尔值(true/false
  • 数值(数字类型)
  • 字符串
  • null
  • 对象(Object)与数组(Array)

不支持的数据类型

  • 函数
  • undefined
  • Symbol(ES6 新增类型)

示例:排除不支持的类型

const invalidData = {  
  func: () => "Hello",  
  undef: undefined,  
  sym: Symbol("id")  
};  
console.log(JSON.stringify(invalidData));  
// 输出:{}  
// 函数、undefined、Symbol 均被忽略  

三、深入参数:replacer 与 space

3.1 参数 replacer:过滤与转换数据

replacer 可以是:

  1. 函数:用于自定义序列化逻辑,例如修改键值对或排除某些属性。
  2. 数组:用于仅保留指定的键。

案例 1:使用数组参数筛选字段

const userData = {  
  id: 101,  
  username: "Alice",  
  password: "secret",  
  email: "alice@example.com"  
};  
// 只保留 username 和 email  
const filteredUser = JSON.stringify(userData, ["username", "email"]);  
console.log(filteredUser);  
// 输出:{"username":"Alice","email":"alice@example.com"}  

案例 2:使用函数参数修改数据

const priceData = {  
  price: 99.99,  
  discount: 0.15  
};  
// 计算折扣后价格并保留两位小数  
const replacerFunction = (key, value) => {  
  if (key === "finalPrice") {  
    return value; // 保留计算后的 finalPrice  
  }  
  if (key === "price") {  
    return value * (1 - priceData.discount); // 应用折扣  
  }  
  return value; // 其他字段保持原样  
};  
// 手动添加 finalPrice 属性  
priceData.finalPrice = priceData.price * (1 - priceData.discount);  
const modifiedJson = JSON.stringify(priceData, replacerFunction);  
console.log(modifiedJson);  
// 输出:{"price":84.993,"discount":0.15,"finalPrice":84.993}  

3.2 参数 space:美化输出格式

space 参数可以是:

  • 数字:表示缩进空格的数量。
  • 字符串:如 " ""\t",用于自定义缩进。

示例:美化 JSON 输出

const config = {  
  server: {  
    host: "localhost",  
    port: 3000,  
    timeout: 5000  
  },  
  logging: {  
    level: "debug"  
  }  
};  
console.log(JSON.stringify(config, null, 2));  
// 输出:  
// {  
//   "server": {  
//     "host": "localhost",  
//     "port": 3000,  
//     "timeout": 5000  
//   },  
//   "logging": {  
//     "level": "debug"  
//   }  
// }  

四、高级用法与常见场景

4.1 处理循环引用(Circular References)

当对象之间存在循环引用时,JSON.stringify() 会抛出错误。例如:

const a = { name: "Node A" };  
const b = { name: "Node B", parent: a };  
a.child = b; // 形成循环:a → b → a  
try {  
  JSON.stringify(a); // 抛出错误:Converting circular structure to JSON  
} catch (e) {  
  console.error(e.message);  
}  

解决方案:在 replacer 函数中检测并跳过循环引用。

function replacer(key, value) {  
  // 检测当前值是否为原始对象(避免无限循环)  
  if (value === a || value === b) {  
    return `{Circular Reference}`; // 替换为占位符或简化表示  
  }  
  return value;  
}  
const safeJson = JSON.stringify(a, replacer);  
console.log(safeJson);  
// 输出:{"name":"Node A","child":"{Circular Reference}"}  

4.2 自定义对象的序列化逻辑

如果对象的原型链上有 toJSON() 方法,JSON.stringify() 会优先调用该方法返回序列化后的值。

class User {  
  constructor(name, email) {  
    this.name = name;  
    this.email = email;  
    this._private = "hidden"; // 私有属性  
  }  
  // 自定义序列化逻辑  
  toJSON() {  
    return {  
      publicName: this.name,  
      contact: this.email  
    };  
  }  
}  
const userInstance = new User("Frank", "frank@example.com");  
console.log(JSON.stringify(userInstance));  
// 输出:{"publicName":"Frank","contact":"frank@example.com"}  
// 私有属性 _private 被自动过滤  

4.3 与 API 交互的典型场景

在前端开发中,JSON.stringify() 经常用于将表单数据转换为 JSON 字符串,以便通过 fetchaxios 发送到后端:

const form = document.querySelector("form");  
form.addEventListener("submit", (e) => {  
  e.preventDefault();  
  const formData = new FormData(form);  
  // 将 FormData 转换为对象  
  const dataObj = Object.fromEntries(formData.entries());  
  // 序列化并发送  
  fetch("/api/submit", {  
    method: "POST",  
    headers: {  
      "Content-Type": "application/json"  
    },  
    body: JSON.stringify(dataObj)  
  });  
});  

五、常见问题与解决方案

5.1 序列化后丢失对象方法

由于 JSON 标准不支持函数类型,对象的方法会被自动忽略。例如:

const objWithMethod = {  
  greeting: "Hello",  
  sayHello() {  
    return this.greeting;  
  }  
};  
console.log(JSON.stringify(objWithMethod));  
// 输出:{"greeting":"Hello"}  
// 方法 sayHello 被移除  

解决方案:如果需要传输函数逻辑,可考虑使用字符串化的代码片段,但需注意安全性风险。

5.2 处理日期对象的序列化

默认情况下,日期对象会被转换为 ISO 格式的字符串:

const date = new Date();  
console.log(JSON.stringify(date));  
// 输出:"2023-10-05T08:00:00.000Z"  

如果需要自定义日期格式,可通过 replacer 函数实现:

function dateReplacer(key, value) {  
  if (value instanceof Date) {  
    return value.toLocaleDateString(); // 转换为本地日期格式  
  }  
  return value;  
}  
const customDateJson = JSON.stringify({ birthdate: new Date() }, dateReplacer);  
// 输出:{"birthdate":"10/5/2023"}  

5.3 性能优化建议

对于大型对象的序列化,建议:

  1. 使用 replacer 过滤不必要的字段,减少数据量。
  2. 避免在循环中频繁调用 JSON.stringify(),可先缓存结果。
  3. 对于超大数据,考虑分块处理或采用二进制格式(如 MessagePack)。

六、总结

JSON.stringify() 是 JavaScript 开发中不可或缺的工具,它简化了数据序列化的复杂性,让开发者能够专注于业务逻辑的实现。通过本文的讲解,读者可以掌握以下核心要点:

  • 理解序列化与反序列化的基础概念
  • 掌握 JSON.stringify() 的基本语法与参数用法
  • 学会处理循环引用、自定义对象及日期等特殊场景
  • 避免常见陷阱并优化性能

在实际开发中,合理使用 JSON.stringify() 可以显著提升数据交互的效率与代码的可维护性。无论是构建前端表单、与 REST API 通信,还是在跨平台应用中共享数据,它都是开发者手中一把灵活的瑞士军刀。


通过本文的学习,相信读者已经能够自信地运用 JSON.stringify() 解决实际问题。记住,实践是掌握技术的最佳途径——不妨尝试将本文的案例复制到代码编辑器中运行,观察不同参数对结果的影响,逐步深化理解。

最新发布