JavaScript JSON 操作指南
JavaScript JSON 操作指南
Introduction
JSON(JavaScript Object Notation)是现代 Web 开发中最流行的数据交换格式。它以简洁的文本形式描述结构化数据,被广泛应用于 API 通信、配置文件存储、前后端数据交换等场景。JavaScript 内置了强大的 JSON 处理能力——JSON.stringify() 和 JSON.parse() 两个方法即可完成绝大多数序列化与反序列化需求。
本文将全面介绍 JavaScript 中 JSON 的操作方法,包括基础语法、编码解码、处理函数与循环引用、实战示例以及常见问题解答。
基础语法
JSON.stringify() - 对象转 JSON 字符串
JSON.stringify(value, replacer, space);
- value:必填,要转换的 JavaScript 对象或值
- replacer:可选,可以是函数或数组,用于过滤或转换属性
- space:可选,用于美化输出的缩进空格数或字符串
JSON.parse() - JSON 字符串转对象
JSON.parse(text, reviver);
- text:必填,要解析的 JSON 字符串
- reviver:可选,解析后对每个属性值进行转换的函数
JSON 的数据类型
JSON 支持以下数据类型:
- 字符串(String):必须用双引号包裹
- 数字(Number):整数或浮点数
- 布尔值(Boolean):true / false
- 空值(Null):null
- 对象(Object):键值对集合,键必须双引号
- 数组(Array):有序列表
注意:JSON 不支持 undefined、函数、Symbol、Date 对象(会转为字符串)等。
代码示例
示例一:基础序列化与反序列化
// 定义一个用户对象
const user = {
name: "小明",
age: 28,
email: "xiaoming@example.com",
isActive: true,
skills: ["JavaScript", "Python", "Go"],
address: {
city: "北京",
district: "朝阳区"
},
createdAt: new Date("2024-01-15")
};
// 序列化为 JSON 字符串
const jsonString = JSON.stringify(user);
console.log("JSON 字符串:");
console.log(jsonString);
// 输出:
// {"name":"小明","age":28,"email":"xiaoming@example.com","isActive":true,"skills":["JavaScript","Python","Go"],"address":{"city":"北京","district":"朝阳区"},"createdAt":"2024-01-15T00:00:00.000Z"}
// 美化输出(带缩进)
const prettyJson = JSON.stringify(user, null, 2);
console.log("\n美化输出:");
console.log(prettyJson);
// 反序列化回对象
const parsedUser = JSON.parse(jsonString);
console.log("\n反序列化后:");
console.log("姓名:", parsedUser.name);
console.log("城市:", parsedUser.address.city);
示例二:使用 replacer 函数进行过滤和转换
const product = {
id: "P001",
name: "无线蓝牙耳机",
price: 299.00,
description: "降噪功能强大",
internalCode: "SKU-NO-EXPORT",
lastUpdated: new Date()
};
// replacer 函数:排除 internalCode 和 lastUpdated,price 保留两位小数
const filteredJson = JSON.stringify(product, function(key, value) {
// 排除特定键
if (key === "internalCode" || key === "lastUpdated") {
return undefined;
}
// price 保留两位小数
if (key === "price") {
return Number(value.toFixed(2));
}
return value;
}, 2);
console.log(filteredJson);
// 输出:
// {
// "id": "P001",
// "name": "无线蓝牙耳机",
// "price": 299,
// "description": "降噪功能强大"
// }
示例三:处理循环引用与函数
// 处理包含函数和 undefined 的对象
const dataWithFunctions = {
title: "测试数据",
callback: function() { console.log("hi"); },
empty: undefined,
symbol: Symbol("test"),
nested: {
value: null,
regex: /\d+/g
}
};
// replacer 过滤掉函数、undefined、symbol
const cleanJson = JSON.stringify(dataWithFunctions, function(key, value) {
if (typeof value === "function" || typeof value === "undefined" || typeof value === "symbol") {
return undefined;
}
if (value instanceof RegExp) {
return value.toString();
}
return value;
}, 2);
console.log(cleanJson);
// {
// "title": "测试数据",
// "nested": {
// "value": null,
// "regex": "/\\d+/g"
// }
// }
// 使用 reviver 在反序列化时转换日期字符串
const jsonWithDate = '{"event":"发布会","date":"2024-12-01T10:00:00.000Z"}';
const parsedWithDate = JSON.parse(jsonWithDate, function(key, value) {
if (key === "date" && typeof value === "string") {
return new Date(value);
}
return value;
});
console.log("日期类型:", parsedWithDate.date instanceof Date); // true
示例四:实战 - LocalStorage 数据缓存
// 保存数据到 LocalStorage
function saveToStorage(key, data) {
try {
const jsonStr = JSON.stringify(data);
localStorage.setItem(key, jsonStr);
console.log("数据已保存:", key);
return true;
} catch (e) {
if (e.name === "QuotaExceededError") {
console.error("存储空间不足");
} else {
console.error("保存失败:", e.message);
}
return false;
}
}
// 从 LocalStorage 读取数据
function loadFromStorage(key) {
try {
const jsonStr = localStorage.getItem(key);
if (!jsonStr) return null;
return JSON.parse(jsonStr);
} catch (e) {
console.error("读取失败:", e.message);
return null;
}
}
// 购物车示例
const cart = [
{ productId: "A001", name: "鼠标", price: 89, qty: 2 },
{ productId: "B002", name: "键盘", price: 299, qty: 1 }
];
saveToStorage("shopping_cart", cart);
// 读取并计算总价
const savedCart = loadFromStorage("shopping_cart");
if (savedCart) {
const total = savedCart.reduce((sum, item) => sum + item.price * item.qty, 0);
console.log("购物车总价:", total); // 89*2 + 299*1 = 477
}
示例五:深拷贝与浅拷贝
// JSON 方法实现深拷贝(局限性:不支持函数、undefined、Symbol、正则、Date 等)
const original = {
name: "测试",
scores: [95, 88, 76],
profile: { level: 5, rank: "钻石" }
};
const deepCopy = JSON.parse(JSON.stringify(original));
original.scores.push(100);
original.profile.level = 99;
console.log("原始:", original.scores); // [95, 88, 76, 100]
console.log("拷贝:", deepCopy.scores); // [95, 88, 76] - 未受影响
console.log("原始 profile:", original.profile.level); // 99
console.log("拷贝 profile:", deepCopy.profile.level); // 5 - 未受影响
// 注意:Date 对象会被转为字符串而非 Date
const withDate = { created: new Date() };
const dateCopy = JSON.parse(JSON.stringify(withDate));
console.log("日期类型:", dateCopy.created instanceof Date); // false
console.log("日期值:", dateCopy.created); // 字符串
运行效果
- 序列化输出:所有对象被转换为标准的 JSON 字符串格式,双引号包裹键名,数组用方括号,对象用花括号。
- 美化输出:通过第三个参数可以输出格式化的 JSON,便于调试和阅读。
- 过滤效果:replacer 函数成功排除了 internalCode、函数、undefined 等不适合 JSON 的值。
- 深拷贝验证:修改原始对象后,拷贝对象的嵌套属性完全不受影响,证明深拷贝成功。
- LocalStorage 持久化:数据在刷新页面后依然可以正确读取和解析。
常见问题
Q1: JSON.parse() 解析失败怎么处理?
JSON.parse() 在遇到无效 JSON 时会抛出 SyntaxError 异常。建议使用 try-catch 包裹:try { JSON.parse(str); } catch (e) { console.error("无效JSON:", e.message); }
Q2: 如何保留 Date 对象而不转为字符串?
标准 JSON 不支持 Date,但可以在序列化时手动转换,或在反序列化时用 reviver 函数还原。例如:JSON.parse(json, (k,v) => k === "date" ? new Date(v) : v)
Q3: JSON.stringify 有哪些局限性?
以下内容会被忽略或转换:undefined、函数、Symbol、NaN 和 Infinity 转为 null、循环引用抛出错误。可以使用 replacer 函数预处理,或考虑使用 flatted、devalue 等第三方库处理复杂场景。
Q4: 如何判断一个字符串是否是有效的 JSON?
可以用 try-catch 包裹 JSON.parse() 判断。也可以使用正则做快速预检(以 { 或 [ 开头),但无法完全替代实际解析。
Q5: JSON.stringify 的 space 参数有什么用?
space 参数控制输出缩进。传入数字表示空格数(最大 10),传入字符串则用该字符串作为缩进字符。设为 "\t" 可使用 Tab 缩进。常用于日志输出、配置文件生成、接口调试等场景。
延伸阅读
- MDN - JSON.stringify():https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
- MDN - JSON.parse():https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse
- JSON Schema:用于验证 JSON 数据结构的规范,适合构建健壮的 API 数据校验层
BigInt.prototype.toJSON() 支持大数字的 JSON 处理Object Model Markup for Debugging 是一个便捷工具