JSON.stringify() 是什么?它为什么这么重要?
在现代前端开发中,数据的传输和存储几乎都离不开 JSON 格式。而 JSON.stringify() 就是将 JavaScript 对象或数组转换成 JSON 字符串的核心工具。你可以把它想象成一个“数据打包机”——把复杂的对象结构,变成一串标准的、能被网络传输或存入本地的文本。
比如你在写一个 Vue 3.0 项目时,需要把用户表单数据发送给后端接口,这时候 JSON.stringify() 就派上用场了。它能帮你把 { name: "张三", age: 25 } 这样的对象,变成 {"name":"张三","age":25} 这种标准格式,让服务器能正确解析。
这个方法是 JavaScript 内置的,不需要引入额外库,使用起来非常方便。但它的功能远不止“简单转换”这么简单——它支持自定义序列化逻辑、处理特殊类型、甚至能避开循环引用问题。
基本用法:从对象到字符串的转换
我们先从最基础的用法开始。假设你有一个用户信息对象:
const user = {
name: "李四",
age: 30,
isActive: true,
hobbies: ["读书", "游泳", "编程"]
};
现在你想把这个对象变成字符串,以便保存到 localStorage 或发送到 API 接口。这时候就可以用 JSON.stringify():
const jsonString = JSON.stringify(user);
console.log(jsonString);
// 输出: {"name":"李四","age":30,"isActive":true,"hobbies":["读书","游泳","编程"]}
📌 关键点:
JSON.stringify()接收一个 JavaScript 值(对象、数组、字符串、数字等),返回一个 JSON 格式的字符串。- 它不会改变原对象,而是返回一个新的字符串。
- 如果传入的是
null或undefined,会返回"null"或直接报错(undefined会忽略,但null会被保留)。
⚠️ 注意:
JSON.stringify()只能处理可序列化的数据类型。像函数、Symbol、undefined 等都无法被序列化。
处理复杂数据结构:数组与嵌套对象
JSON.stringify() 不仅能处理扁平对象,还能处理嵌套结构。比如一个包含多个用户的列表:
const users = [
{
id: 1,
name: "王五",
profile: {
city: "北京",
skills: ["JavaScript", "Vue 3.0", "Node.js"]
}
},
{
id: 2,
name: "赵六",
profile: {
city: "上海",
skills: ["Python", "Django", "Docker"]
}
}
];
const jsonUsers = JSON.stringify(users);
console.log(jsonUsers);
// 输出: [{"id":1,"name":"王五","profile":{"city":"北京","skills":["JavaScript","Vue 3.0","Node.js"]}}, {"id":2,"name":"赵六","profile":{"city":"上海","skills":["Python","Django","Docker"]}}]
这个结果非常清晰,结构完整,适合在前后端之间传递。
嵌套结构的“包装”比喻
你可以把 JSON.stringify() 想象成一个快递打包员:
- 每个对象就像一个包裹,里面可能还有小盒子(嵌套对象)。
- 打包员会一层层地把所有内容“贴上标签”,变成一串标准化的字符串,确保运输过程中不会被拆散。
第二个参数:replacer(序列化过滤器)
有时候你并不想把所有数据都传出去。比如用户对象里有敏感信息,如密码、token 等,就不该被序列化。
这时 JSON.stringify() 的第二个参数 replacer 就非常有用。它允许你指定哪些属性应该被包含或排除。
1. 使用数组过滤字段
const user = {
name: "孙七",
password: "123456",
token: "abc123xyz",
email: "sunqi@example.com",
age: 28
};
// 只保留 name 和 email 字段
const filteredJson = JSON.stringify(user, ["name", "email"]);
console.log(filteredJson);
// 输出: {"name":"孙七","email":"sunqi@example.com"}
✅ 优点:简单直接,适合固定字段筛选。
2. 使用函数自定义过滤逻辑
更灵活的方式是传入一个函数,由你决定每个键值对是否保留:
const user = {
name: "周八",
password: "secret",
token: "xyz789",
email: "zhouba@example.com",
age: 32
};
const json = JSON.stringify(user, function (key, value) {
// 如果是 password 或 token,返回 undefined,表示忽略
if (key === "password" || key === "token") {
return undefined;
}
// 其他字段原样返回
return value;
});
console.log(json);
// 输出: {"name":"周八","email":"zhouba@example.com","age":32}
📌 这个技巧在开发 API 接口时特别实用,可以避免敏感信息泄露。
第三个参数:space(格式化输出)
默认情况下,JSON.stringify() 生成的字符串是紧凑的,没有换行和缩进。这在传输时很高效,但不利于人类阅读。
如果你希望输出更美观的格式,可以用第三个参数 space:
const data = {
title: "我的笔记",
items: [
{ id: 1, content: "学习 JSON" },
{ id: 2, content: "练习 stringify" }
],
author: "开发者小张"
};
// 使用 2 个空格进行缩进
const formattedJson = JSON.stringify(data, null, 2);
console.log(formattedJson);
输出结果如下:
{
"title": "我的笔记",
"items": [
{
"id": 1,
"content": "学习 JSON"
},
{
"id": 2,
"content": "练习 stringify"
}
],
"author": "开发者小张"
}
📌 space 支持:
- 数字:表示空格数(如 2、4)
- 字符串:如
" "(两个空格)、"->"(自定义缩进符)
这个功能在调试阶段非常有用,能让你快速查看数据结构。
特殊值的处理:null、undefined、函数、Symbol
JSON.stringify() 对某些特殊值有明确的处理规则。我们来看几个常见情况:
| JavaScript 值 | JSON.stringify() 结果 | 说明 |
|---|---|---|
null |
"null" |
会被保留为字符串 "null" |
undefined |
被忽略(不参与序列化) | 不会出现在最终字符串中 |
function |
undefined(被忽略) |
函数无法序列化 |
Symbol |
undefined(被忽略) |
Symbol 类型不支持 JSON |
NaN、Infinity |
null |
会被转为 null |
实际示例:
const obj = {
a: null,
b: undefined,
c: function () {},
d: Symbol("id"),
e: NaN,
f: Infinity
};
const result = JSON.stringify(obj);
console.log(result);
// 输出: {"a":null,"e":null,"f":null}
📌 从结果可以看到:
null保留了undefined、function、Symbol都被忽略NaN和Infinity被转为null
⚠️ 重要提醒:如果你的代码中依赖 undefined 作为字段存在,JSON.stringify() 会丢失它!所以要提前做好字段处理。
循环引用问题:如何避免死循环?
在复杂对象中,有时会出现循环引用,比如 A 引用了 B,B 又引用了 A。
const a = { name: "A" };
const b = { name: "B" };
a.ref = b;
b.ref = a; // 循环引用
// 这样会抛出错误!
// JSON.stringify(a); // TypeError: Converting circular structure to JSON
这时 JSON.stringify() 会报错:Converting circular structure to JSON。
解决方案:使用 replacer + 弱引用检测
我们可以用 replacer 函数来检测循环引用,避免递归死循环:
const seen = new WeakSet();
const result = JSON.stringify(a, function (key, value) {
if (typeof value === "object" && value !== null) {
if (seen.has(value)) {
return; // 如果已经见过,跳过
}
seen.add(value);
}
return value;
});
console.log(result);
// 输出: {"name":"A","ref":{"name":"B","ref":{}}}
📌 这个技巧在处理复杂数据模型时非常实用,比如 Vue 3.0 的响应式对象、React 的组件树等。
实际应用:本地存储与 API 通信
1. 本地存储(localStorage)
const userInfo = {
username: "admin",
preferences: { theme: "dark", language: "zh-CN" }
};
// 存储前序列化
localStorage.setItem("user", JSON.stringify(userInfo));
// 读取后反序列化
const stored = localStorage.getItem("user");
const parsed = JSON.parse(stored); // 用 JSON.parse 反解
console.log(parsed.username); // "admin"
2. 发送请求到后端
const postData = {
title: "新文章",
content: "这是第一篇技术文章。",
tags: ["JavaScript", "JSON"]
};
fetch("/api/posts", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(postData) // 必须用 stringify
})
.then(response => response.json())
.then(data => console.log("提交成功", data));
📌 在网络请求中,Content-Type 必须是 application/json,而 body 必须是字符串——这就是 JSON.stringify() 的用武之地。
总结:掌握 JSON.stringify(),就是掌握数据流动的钥匙
JSON.stringify() 看似简单,实则功能强大。它不仅是数据转换的工具,更是前后端协作、本地存储、调试排查的重要环节。
从基础转换到过滤字段,从格式化输出到处理循环引用,每一个细节都值得你深入理解。尤其在现代开发中,无论是 Vue 3.0、React,还是 Node.js 服务端,都离不开它。
记住:
- 它能处理对象、数组,但不能处理函数、Symbol。
- 使用
replacer可以精确控制哪些字段被包含。 - 使用
space让输出更易读。 - 遇到循环引用,用
WeakSet检测避免崩溃。
掌握这些技巧,你就能在开发中游刃有余地处理各种数据结构问题。下次遇到“数据传不过去”“接口返回空”时,不妨先检查一下是不是少了 JSON.stringify() 这一步。