11.JavaScript JSON 详解
📋 JSON 与对象相互转化方法速查表
| 方法 | 方向 | 功能 | 示例 | 返回值 |
|---|---|---|---|---|
JSON.stringify() |
对象 → JSON | 将JavaScript对象转换为JSON字符串 | JSON.stringify({name: "张三"}) |
'{"name":"张三"}' |
JSON.parse() |
JSON → 对象 | 将JSON字符串转换为JavaScript对象 | JSON.parse('{"name":"张三"}') |
{name: "张三"} |
JSON.stringify() 带参数 |
对象 → JSON(格式化) | 格式化输出JSON字符串 | JSON.stringify(obj, null, 2) |
格式化的JSON字符串 |
toJSON() 方法 |
自定义序列化 | 对象自定义序列化逻辑 | obj.toJSON() |
自定义的JSON数据 |
一、什么是JSON?
1. JSON 的简单解释
JSON(JavaScript Object Notation)是一种数据格式,就像两个人用同一种语言交流一样,不同的程序之间用JSON来传递数据。
// JSON 看起来很像 JavaScript 对象,但有严格规则:
let jsonString = '{"name": "张三", "age": 25, "isStudent": true}';
// JavaScript 对象:
let jsObject = {name: "张三", age: 25, isStudent: true};
2. JSON 的三大特点
- 轻量级:文件小,传输快
- 易读:人类和机器都能看懂
- 独立:不依赖任何编程语言
二、JSON 的基本语法规则
1. JSON 的组成
{
"name": "张三", // 键值对(键必须用双引号)
"age": 25, // 数字(不用引号)
"isStudent": true, // 布尔值(true/false,不用引号)
"hobbies": ["篮球", "音乐", "阅读"], // 数组
"address": { // 嵌套对象
"city": "北京",
"street": "长安街"
},
"score": null // null(空值)
}
2. JSON 与 JavaScript 对象的区别
// ✅ JSON(字符串格式)
let json = '{"name": "张三", "age": 25}';
// ✅ JavaScript对象(内存中的数据结构)
let obj = {name: "张三", age: 25};
// ❌ JSON 不允许的:
// 1. 键名不用引号
// '{"name": "张三"}' ✅(JSON必须用双引号)
// '{name: "张三"}' ❌(JSON不能用单引号或无引号)
// 2. 单引号字符串
// '{"name": "张三"}' ✅
// "{'name': '张三'}" ❌(JSON字符串必须用双引号)
// 3. 尾部逗号
// '{"name": "张三"}' ✅
// '{"name": "张三",}' ❌(最后一个键值对后不能有逗号)
// 4. 函数、undefined等特殊值
// JSON.stringify({func: function(){}}) // '{}'(函数会被忽略)
三、JSON.stringify() – 对象转JSON
1. 基本用法
let person = {
name: "张三",
age: 25,
isStudent: true,
hobbies: ["篮球", "音乐"],
address: {
city: "北京",
street: "长安街"
}
};
// 转换为JSON字符串
let jsonString = JSON.stringify(person);
console.log(jsonString);
// 输出:'{"name":"张三","age":25,"isStudent":true,"hobbies":["篮球","音乐"],"address":{"city":"北京","street":"长安街"}}'
2. 过滤属性(第二个参数)
let person = {
name: "张三",
age: 25,
password: "123456",
email: "zhangsan@example.com"
};
// 只转换指定的属性
let json1 = JSON.stringify(person, ["name", "age"]);
console.log(json1); // '{"name":"张三","age":25}'
// 使用函数过滤
let json2 = JSON.stringify(person, function(key, value) {
if (key === "password") {
return undefined; // 不包含密码
}
return value; // 包含其他属性
});
console.log(json2); // '{"name":"张三","age":25,"email":"zhangsan@example.com"}'
3. 格式化输出(第三个参数)
let person = {name: "张三", age: 25, hobbies: ["篮球", "音乐"]};
// 缩进2个空格
let prettyJson = JSON.stringify(person, null, 2);
console.log(prettyJson);
/*
输出:
{
"name": "张三",
"age": 25,
"hobbies": [
"篮球",
"音乐"
]
}
*/
// 使用制表符缩进
let tabJson = JSON.stringify(person, null, "t");
console.log(tabJson);
// 使用自定义缩进字符
let customJson = JSON.stringify(person, null, "---");
console.log(customJson);
/*
{
---"name": "张三",
---"age": 25,
---"hobbies": [
------"篮球",
------"音乐"
---]
}
*/
4. toJSON() 方法(自定义序列化)
let person = {
name: "张三",
age: 25,
birthDate: new Date("1999-01-15"),
// 自定义JSON序列化
toJSON: function() {
return {
name: this.name,
age: this.age,
birthYear: this.birthDate.getFullYear(),
type: "person"
};
}
};
console.log(JSON.stringify(person));
// '{"name":"张三","age":25,"birthYear":1999,"type":"person"}'
5. 处理特殊值
let data = {
name: "张三",
age: undefined, // undefined会被忽略
func: function() {}, // 函数会被忽略
symbol: Symbol("id"), // Symbol会被忽略
date: new Date(), // Date会被转为字符串
infinity: Infinity, // Infinity, NaN, null会被转为null
nan: NaN,
nullValue: null,
array: [1, undefined, function() {}] // 数组中的undefined和函数会变成null
};
console.log(JSON.stringify(data));
// '{"name":"张三","date":"2024-01-15T10:30:00.000Z","infinity":null,"nan":null,"nullValue":null,"array":[1,null,null]}'
四、JSON.parse() – JSON转对象
1. 基本用法
let jsonString = '{"name": "张三", "age": 25, "isStudent": true}';
// 转换为JavaScript对象
let person = JSON.parse(jsonString);
console.log(person.name); // "张三"
console.log(person.age); // 25
console.log(person.isStudent); // true
console.log(typeof person); // "object"
2. 解析函数(第二个参数)
let jsonString = '{"name": "张三", "age": 25, "birthDate": "1999-01-15"}';
// 使用reviver函数转换值
let person = JSON.parse(jsonString, function(key, value) {
if (key === "birthDate") {
return new Date(value); // 将字符串转为Date对象
}
if (key === "age") {
return value + "岁"; // 修改age的值
}
return value; // 其他值保持不变
});
console.log(person.birthDate.getFullYear()); // 1999
console.log(person.age); // "25岁"
3. 错误处理
// ❌ 无效的JSON会报错
try {
let invalidJson = '{name: "张三"}'; // 键名没用双引号
let obj = JSON.parse(invalidJson);
} catch (error) {
console.log("JSON解析错误:", error.message);
// 输出:JSON解析错误: Expected property name or '}' in JSON at position 1
}
// ❌ 尾部逗号
try {
let invalidJson = '{"name": "张三",}';
let obj = JSON.parse(invalidJson);
} catch (error) {
console.log("JSON解析错误:", error.message);
}
// ✅ 正确写法
let validJson = '{"name": "张三"}';
let obj = JSON.parse(validJson); // 正常执行
五、实际应用场景
1. 存储数据到本地存储
// 存储用户数据到localStorage
let user = {
id: 1,
name: "张三",
preferences: {
theme: "dark",
language: "zh-CN"
}
};
// 存储(对象转JSON字符串)
localStorage.setItem("user", JSON.stringify(user));
// 读取(JSON字符串转对象)
let savedUser = JSON.parse(localStorage.getItem("user"));
console.log(savedUser.name); // "张三"
2. 与服务器通信
// 模拟从服务器获取数据
function fetchUserData(userId) {
// 假设这是从服务器返回的JSON字符串
let response = '{"id": 1, "name": "张三", "email": "zhangsan@example.com"}';
// 解析为JavaScript对象
let userData = JSON.parse(response);
// 使用数据
console.log(`欢迎回来,${userData.name}`);
console.log(`您的邮箱:${userData.email}`);
return userData;
}
// 模拟发送数据到服务器
function saveUserData(user) {
// 将对象转为JSON字符串
let jsonData = JSON.stringify(user);
// 发送到服务器(模拟)
console.log("发送到服务器:", jsonData);
return true;
}
// 使用示例
let user = {name: "李四", age: 30};
saveUserData(user);
3. 配置文件管理
// 配置文件(通常是JSON格式)
let configJson = `
{
"appName": "我的应用",
"version": "1.0.0",
"settings": {
"maxUsers": 100,
"theme": "light",
"features": ["search", "filter", "export"]
},
"apiEndpoints": {
"login": "/api/login",
"users": "/api/users"
}
}
`;
// 解析配置文件
let config = JSON.parse(configJson);
// 使用配置
console.log(`应用:${config.appName} ${config.version}`);
console.log(`主题:${config.settings.theme}`);
console.log(`API地址:${config.apiEndpoints.login}`);
4. 深度克隆对象
// JSON方法实现深度克隆(简单对象)
let original = {
name: "张三",
age: 25,
address: {
city: "北京",
street: "长安街"
}
};
// 深度克隆
let clone = JSON.parse(JSON.stringify(original));
// 修改克隆对象不会影响原对象
clone.address.city = "上海";
console.log(original.address.city); // "北京"(未改变)
console.log(clone.address.city); // "上海"(已改变)
// ⚠️ 注意:这种方法不能克隆函数、undefined等特殊值
let complexObj = {
func: function() {},
date: new Date(),
undefinedValue: undefined
};
let badClone = JSON.parse(JSON.stringify(complexObj));
console.log(badClone.func); // undefined(函数丢失)
console.log(badClone.undefinedValue); // undefined(但实际是属性不存在)
六、JSON 的局限性
1. 不支持的数据类型
let data = {
// ❌ JSON不支持的类型
func: function() {}, // 函数
undefinedValue: undefined, // undefined
symbol: Symbol("id"), // Symbol
bigInt: 123n, // BigInt
date: new Date(), // Date(会被转成字符串)
// ✅ JSON支持的类型
string: "hello", // 字符串
number: 123, // 数字
boolean: true, // 布尔值
nullValue: null, // null
array: [1, 2, 3], // 数组
object: {key: "value"} // 对象
};
let json = JSON.stringify(data);
console.log(json); // 只有支持的类型被序列化
2. 循环引用问题
// ❌ 循环引用会导致错误
let objA = {name: "A"};
let objB = {name: "B"};
objA.ref = objB; // A引用B
objB.ref = objA; // B引用A(循环引用)
try {
let json = JSON.stringify(objA); // 报错!
} catch (error) {
console.log("错误:", error.message); // Converting circular structure to JSON
}
// ✅ 解决方案:处理循环引用
function safeStringify(obj, indent = 2) {
let cache = new Set();
return JSON.stringify(obj, function(key, value) {
if (typeof value === "object" && value !== null) {
if (cache.has(value)) {
return; // 遇到重复引用,返回undefined(会被忽略)
}
cache.add(value);
}
return value;
}, indent);
}
console.log(safeStringify(objA));
七、进阶技巧
1. 自定义序列化复杂对象
class User {
constructor(name, birthDate) {
this.name = name;
this.birthDate = new Date(birthDate);
}
get age() {
let today = new Date();
return today.getFullYear() - this.birthDate.getFullYear();
}
// 自定义JSON序列化
toJSON() {
return {
name: this.name,
birthDate: this.birthDate.toISOString().split('T')[0],
age: this.age,
type: "User"
};
}
}
let user = new User("张三", "1999-05-20");
console.log(JSON.stringify(user));
// '{"name":"张三","birthDate":"1999-05-20","age":25,"type":"User"}'
2. 解析时恢复对象类型
let data = {
users: [
{
name: "张三",
birthDate: "1999-05-20",
type: "User"
},
{
name: "李四",
birthDate: "2000-08-15",
type: "User"
}
],
created: "2024-01-15T10:30:00.000Z"
};
let jsonString = JSON.stringify(data);
// 解析并恢复类型
let parsed = JSON.parse(jsonString, function(key, value) {
if (key === "birthDate" || key === "created") {
return new Date(value);
}
if (value && value.type === "User") {
// 这里可以恢复为User类的实例
return value;
}
return value;
});
console.log(parsed.created.getFullYear()); // 2024
console.log(parsed.users[0].birthDate instanceof Date); // true
3. JSON Schema验证
// 简单的JSON Schema验证函数
function validateJSON(schema, jsonString) {
try {
let data = JSON.parse(jsonString);
// 检查必需字段
if (schema.required) {
for (let field of schema.required) {
if (!(field in data)) {
throw new Error(`缺少必需字段: ${field}`);
}
}
}
// 检查字段类型
if (schema.properties) {
for (let [field, prop] of Object.entries(schema.properties)) {
if (data[field] !== undefined) {
if (prop.type && typeof data[field] !== prop.type) {
throw new Error(`字段 ${field} 应为 ${prop.type} 类型`);
}
}
}
}
return {valid: true, data};
} catch (error) {
return {valid: false, error: error.message};
}
}
// 定义Schema
let userSchema = {
required: ["name", "age"],
properties: {
name: {type: "string"},
age: {type: "number"},
email: {type: "string"}
}
};
// 测试验证
let result1 = validateJSON(userSchema, '{"name": "张三", "age": 25}');
console.log(result1.valid); // true
let result2 = validateJSON(userSchema, '{"name": "张三"}');
console.log(result2.valid); // false(缺少age)
八、性能优化建议
1. 大JSON文件处理
// ❌ 不推荐:一次性解析大JSON
let hugeData = JSON.parse(veryLargeJsonString); // 可能内存溢出
// ✅ 推荐:使用流式处理(如果可能)
// 或分割为小块处理
// 使用reviver函数进行增量处理
function parseLargeJSON(jsonString, chunkCallback) {
let data = JSON.parse(jsonString, function(key, value) {
// 每次解析一个属性时调用回调
chunkCallback(key, value);
return value;
});
return data;
}
2. 缓存解析结果
let jsonCache = new Map();
function parseWithCache(jsonString) {
if (jsonCache.has(jsonString)) {
return jsonCache.get(jsonString);
}
let result = JSON.parse(jsonString);
jsonCache.set(jsonString, result);
return result;
}
// 多次解析同一个JSON时,使用缓存提高性能
3. 避免重复序列化
// ❌ 不推荐:在循环中重复序列化
for (let item of items) {
let json = JSON.stringify(item); // 每次循环都序列化
// ...
}
// ✅ 推荐:批量序列化
let jsonArray = items.map(item => JSON.stringify(item));
// 或
let allJson = JSON.stringify(items); // 一次性序列化整个数组
九、总结与最佳实践
1. 安全注意事项
// ❌ 危险:直接解析不可信的JSON
let dangerousJson = '{"__proto__": {"admin": true}}';
let obj = JSON.parse(dangerousJson); // 可能污染原型链
// ✅ 安全:使用reviver函数过滤
let safeObj = JSON.parse(dangerousJson, function(key, value) {
if (key === "__proto__") {
return undefined; // 过滤危险属性
}
return value;
});
2. 错误处理最佳实践
function safeJSONParse(jsonString, defaultValue = null) {
try {
return JSON.parse(jsonString);
} catch (error) {
console.error("JSON解析失败:", error.message, "原始字符串:", jsonString);
return defaultValue;
}
}
function safeJSONStringify(obj, defaultValue = "{}") {
try {
return JSON.stringify(obj);
} catch (error) {
console.error("JSON序列化失败:", error.message, "原始对象:", obj);
return defaultValue;
}
}
// 使用安全函数
let data = safeJSONParse(invalidJson, {});
let json = safeJSONStringify(circularObject, "{}");
3. 实用工具函数
const JSONUtils = {
// 深度克隆(处理循环引用)
deepClone: function(obj) {
let visited = new WeakMap();
function clone(value) {
if (value === null || typeof value !== "object") {
return value;
}
// 处理循环引用
if (visited.has(value)) {
return visited.get(value);
}
// 处理数组
if (Array.isArray(value)) {
let arrClone = [];
visited.set(value, arrClone);
arrClone.push(...value.map(clone));
return arrClone;
}
// 处理普通对象
let objClone = {};
visited.set(value, objClone);
for (let key in value) {
if (value.hasOwnProperty(key)) {
objClone[key] = clone(value[key]);
}
}
return objClone;
}
return clone(obj);
},
// 美化JSON字符串
prettyPrint: function(obj, indent = 2) {
return JSON.stringify(obj, null, indent);
},
// 压缩JSON字符串(移除所有空格)
minify: function(jsonString) {
return jsonString.replace(/s+/g, "");
},
// 判断是否为有效JSON
isValidJSON: function(str) {
try {
JSON.parse(str);
return true;
} catch {
return false;
}
}
};
// 使用示例
let obj = {a: 1, b: {c: 2}};
console.log(JSONUtils.prettyPrint(obj));
console.log(JSONUtils.deepClone(obj));
console.log(JSONUtils.isValidJSON('{"a":1}')); // true
4. 记忆口诀
JSON是字符串,对象是数据结构
stringify 对象转字符串,常用三个参数
parse 字符串转对象,注意错误处理
键名必须双引号,单引号不支持
函数undefined不能存,日期会变字符串
循环引用会报错,深度克隆要小心
本地存储常用到,前后端通信少不了
掌握了JSON的使用,你就掌握了现代Web开发中数据交换的核心技能!🎯







