JavaScript JSON 操作指南

小飞兽 JavaScript 382 次阅读 2026-05-12

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 函数预处理,或考虑使用 flatteddevalue 等第三方库处理复杂场景。

    Q4: 如何判断一个字符串是否是有效的 JSON?

    可以用 try-catch 包裹 JSON.parse() 判断。也可以使用正则做快速预检(以 {[ 开头),但无法完全替代实际解析。

    Q5: JSON.stringify 的 space 参数有什么用?

    space 参数控制输出缩进。传入数字表示空格数(最大 10),传入字符串则用该字符串作为缩进字符。设为 "\t" 可使用 Tab 缩进。常用于日志输出、配置文件生成、接口调试等场景。

    延伸阅读

  • BigInt 的 JSON 序列化:ES2020 引入 BigInt.prototype.toJSON() 支持大数字的 JSON 处理
  • OMMD:如果你需要分享数据结构可视化,Object Model Markup for Debugging 是一个便捷工具