11.JavaScript JSON 详解

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开发中数据交换的核心技能!🎯

shi著
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇