在现代 Web 开发中,数据交换是不可或缺的一部分。无论是客户端与服务器之间的数据传输,还是不同应用程序之间的数据共享,都需要一种高效、易读且通用的数据格式。在这里,JSON (JavaScript Object Notation) 应运而生,并迅速成为最流行的数据交换格式之一。

JSON 是一种轻量级的数据交换格式,它基于 JavaScript 编程语言的一个子集,但独立于语言。它使用人类可读的文本来表示结构化数据,使得数据传输和解析变得异常简单。JavaScript 对 JSON 提供了原生支持,使得在 JavaScript 环境中处理 JSON 数据尤为方便。

本教程将深入探讨 JSON 的基本概念、语法规则、支持的数据类型,以及如何在 JavaScript 中有效地解析 (parse) 和序列化 (stringify) JSON 数据。通过学习本教程,您将能够熟练地在 JavaScript 应用程序中利用 JSON 进行高效的数据管理和通信。


前置知识

在开始本教程之前,建议您具备以下 JavaScript 基础知识:

  • 基本数据类型: 熟悉 JavaScript 中的字符串、数字、布尔值和 null
  • 对象 (Objects): 了解 JavaScript 对象的创建、属性访问和修改。
  • 数组 (Arrays): 熟悉 JavaScript 数组的创建、元素访问和遍历。

目录

  1. JSON 简介
    • 1.1 什么是 JSON?
    • 1.2 为什么 JSON 如此重要?
  2. JSON 的语法规则
    • 2.1 数据是名称/值对
    • 2.2 数据通过逗号分隔
    • 2.3 花括号 ({}) 保存对象
    • 2.4 方括号 ([]) 保存数组
    • 2.5 键必须是字符串 (双引号)
  3. JSON 支持的数据类型
    • 3.1 字符串 (String)
    • 3.2 数字 (Number)
    • 3.3 布尔值 (Boolean)
    • 3.4 Null
    • 3.5 对象 (Object)
    • 3.6 数组 (Array)
    • 3.7 不支持的数据类型
  4. 在 JavaScript 中处理 JSON
    • 4.1 将 JSON 字符串解析为 JavaScript 对象:JSON.parse()
      • 4.1.1 基本用法
      • 4.1.2 带有 reviver 函数
      • 4.1.3 错误处理
    • 4.2 将 JavaScript 对象序列化为 JSON 字符串:JSON.stringify()
      • 4.2.1 基本用法
      • 4.2.2 带有 replacer 参数
        • 数组形式的 replacer
        • 函数形式的 replacer
      • 4.2.3 带有 space 参数
      • 4.2.4 JSON.stringify() 的行为和限制
  5. JSON 的常见应用场景
    • 5.1 Web API 数据传输
    • 5.2 配置文件
    • 5.3 本地存储 (LocalStorage/SessionStorage)
  6. JSON 与 JavaScript 对象的区别
  7. 总结
  8. 附录:术语表

1. JSON 简介

1.1 什么是 JSON?

JSON (JavaScript Object Notation),顾名思义,是“JavaScript 对象表示法”。它是一种轻量级的数据交换格式,完全独立于编程语言,但其格式灵感来源于 JavaScript 对象的字面量语法。

JSON 旨在成为一种易于人类阅读和编写,同时也易于机器解析和生成的文本格式。它通常用于服务器与 Web 应用程序之间交换数据。

核心特点:

  • 轻量级: 相较于 XML 等其他数据交换格式,JSON 拥有更小的文件体积。
  • 文本格式: JSON 数据是纯文本,这意味着它可以在任何程序和平台之间轻松传输。
  • 自描述: JSON 的结构清晰,易于理解其数据含义。
  • 语言无关: 虽然源自 JavaScript,但几乎所有主流编程语言都支持 JSON 的解析和生成。
1.2 为什么 JSON 如此重要?

在当今的 Web 开发中,JSON 扮演着至关重要的角色:

  • API 数据交互: 绝大多数 RESTful API 都使用 JSON 作为请求体和响应体的数据格式。
  • 配置文件: 许多现代应用程序和工具(如 Node.js 项目的 package.json)使用 JSON 来存储配置信息。
  • 数据存储: 某些 NoSQL 数据库(如 MongoDB)直接使用 JSON 或其变体 BSON 来存储数据。
  • 简化开发: 由于其简洁的语法和 JavaScript 的原生支持,开发者可以非常高效地处理 JSON 数据。

2. JSON 的语法规则

JSON 的语法是 JavaScript 对象字面量语法的严格子集。理解这些规则是正确使用 JSON 的前提。

2.1 数据是名称/值对

JSON 数据由一系列名称/值对 (name/value pairs) 组成。

  • 名称(或称“键”)必须是字符串,并用双引号 (") 包裹。
  • 值可以是 JSON 支持的任何数据类型(字符串、数字、布尔值、null、对象、数组)。
  • 名称和值之间用冒号 (:) 分隔。

示例:

"firstName": "John"
2.2 数据通过逗号分隔

如果一个 JSON 对象或数组包含多个名称/值对或多个元素,它们之间必须用逗号 (,) 分隔。

示例:

{
  "firstName": "John",
  "lastName": "Doe",
  "age": 30
}
2.3 花括号 ({}) 保存对象

JSON 对象是无序的名称/值对集合。它以左花括号 ({) 开始,以右花括号 (}) 结束。

示例:

{
  "name": "Alice",
  "age": 25,
  "isStudent": true
}
2.4 方括号 ([]) 保存数组

JSON 数组是值的有序集合。它以左方括号 ([) 开始,以右方括号 (]) 结束。数组中的值可以是 JSON 支持的任何数据类型。

示例:

[
  "apple",
  "banana",
  "cherry"
]

数组也可以包含对象:

[
  { "name": "Item 1", "price": 10 },
  { "name": "Item 2", "price": 20 }
]
2.5 键必须是字符串 (双引号)

这是 JSON 与 JavaScript 对象字面量的一个重要区别。在 JSON 中,所有键(属性名)都必须是双引号引起来的字符串。在 JavaScript 对象中,如果键是有效的标识符,则可以不加引号或使用单引号。

JSON 中合法:

{
  "name": "Bob",
  "age": 40
}

JSON 中非法 (但 JavaScript 对象中合法):

// { name: "Bob" }       <-- 键没有双引号
// { 'name': "Bob" }     <-- 键使用单引号

3. JSON 支持的数据类型

JSON 支持以下六种数据类型的值:

3.1 字符串 (String)
  • 由双引号包裹的零个或多个 Unicode 字符。
  • 支持反斜杠转义序列(例如 \n\t\\\")。

示例: "Hello, World!", "\"Special\" Character"

3.2 数字 (Number)
  • 整型或浮点型数字。
  • 不允许有前导零(除非是0本身),不允许使用八进制或十六进制表示。
  • 不支持 NaNInfinity

示例: 10, 3.14, -5, 0

3.3 布尔值 (Boolean)
  • truefalse

示例: true, false

3.4 Null
  • 表示空值。

示例: null

3.5 对象 (Object)
  • 无序的键/值对集合,用花括号 {} 包裹。

示例: {"key": "value", "anotherKey": 123}

3.6 数组 (Array)
  • 有序的值集合,用方括号 [] 包裹。

示例: ["apple", "banana", "orange"], [1, true, {"id": 1}]

3.7 不支持的数据类型

以下 JavaScript 数据类型不被 JSON 支持

  • 函数 (Functions)
  • 日期对象 (Date objects)
  • undefined
  • Symbol
  • BigInt

当 JavaScript 对象包含这些类型时,使用 JSON.stringify() 进行序列化时,它们会被忽略或转换为 null(例如函数或 undefined 作为对象属性值会被忽略,在数组中则会变为 null)。日期对象会被转换为 ISO 格式的字符串。


4. 在 JavaScript 中处理 JSON

JavaScript 提供了内置的 JSON 全局对象,包含了两个核心方法用于 JSON 和 JavaScript 对象之间的转换:JSON.parse()JSON.stringify()

4.1 将 JSON 字符串解析为 JavaScript 对象:JSON.parse()

JSON.parse() 方法用于将一个 JSON 格式的字符串转换为相应的 JavaScript 值或对象。

4.1.1 基本用法
const jsonString = '{"name": "Alice", "age": 30, "city": "New York"}';

const jsObject = JSON.parse(jsonString);

console.log(jsObject);            // { name: 'Alice', age: 30, city: 'New York' }
console.log(jsObject.name);       // Alice
console.log(typeof jsObject);     // object

const jsonArrayString = '[10, 20, "hello", true]';
const jsArray = JSON.parse(jsonArrayString);
console.log(jsArray);             // [10, 20, "hello", true]
console.log(jsArray[2]);          // hello
4.1.2 带有 reviver 函数

JSON.parse() 可以接受第二个可选参数 reviverreviver 是一个函数,它会遍历转换后的 JavaScript 对象的所有键值对,并在返回前对值进行修改。这在数据恢复和类型转换时非常有用(例如,将 ISO 格式的日期字符串转换为 Date 对象)。

reviver 函数的签名是 (key, value) => modifiedValue

const jsonWithDate = '{"product": "Laptop", "price": 1200, "purchaseDate": "2023-10-26T10:00:00.000Z"}';

const data = JSON.parse(jsonWithDate, (key, value) => {
  if (key === 'purchaseDate') {
    return new Date(value); // 将日期字符串转换为 Date 对象
  }
  return value; // 保持其他值不变
});

console.log(data); // { product: 'Laptop', price: 1200, purchaseDate: Thu Oct 26 2023... }
console.log(data.purchaseDate instanceof Date); // true
4.1.3 错误处理

如果 JSON.parse() 接收到的字符串不是一个合法的 JSON 格式,它会抛出一个 SyntaxError 异常。因此,在使用时,通常建议将其放在 try...catch 块中。

const malformedJson = '{"name": "Bob", "age": 40,}'; // 尾部逗号不合法
const anotherMalformed = '{name: "Charlie"}';       // 键没有双引号

try {
  JSON.parse(malformedJson);
} catch (error) {
  console.error("解析错误:", error.message); // 解析错误: Unexpected token } in JSON at position 25
}

try {
    JSON.parse(anotherMalformed);
} catch (error) {
    console.error("解析错误:", error.message); // 解析错误: Expected property name or '}' in JSON at position 1
}
4.2 将 JavaScript 对象序列化为 JSON 字符串:JSON.stringify()

JSON.stringify() 方法用于将一个 JavaScript 值(通常是对象或数组)转换为 JSON 格式的字符串。

4.2.1 基本用法
const jsObject = {
  firstName: "Jane",
  lastName: "Doe",
  age: 28,
  hobbies: ["reading", "hiking"],
  address: {
    street: "123 Main St",
    city: "Anytown"
  }
};

const jsonString = JSON.stringify(jsObject);

console.log(jsonString);
// 输出: {"firstName":"Jane","lastName":"Doe","age":28,"hobbies":["reading","hiking"],"address":{"street":"123 Main St","city":"Anytown"}}
console.log(typeof jsonString); // string
4.2.2 带有 replacer 参数

JSON.stringify() 可以接受第二个可选参数 replacerreplacer 可以是一个数组或一个函数,用于控制哪些属性应该被包含在 JSON 字符串中,以及它们的序列化方式。

  • 数组形式的 replacer
    如果 replacer 是一个数组,它指定了应该包含在 JSON 字符串中的属性名称。只有这些属性会被序列化。

    const userInfo = {
      name: "Alice",
      age: 25,
      email: "alice@example.com",
      password: "securepassword123" // 不希望序列化
    };
    
    const publicInfoJson = JSON.stringify(userInfo, ["name", "age", "email"]);
    console.log(publicInfoJson); // {"name":"Alice","age":25,"email":"alice@example.com"}
    
  • 函数形式的 replacer
    如果 replacer 是一个函数,它会为每个属性(包括父对象本身)调用。该函数接收两个参数:key (属性名) 和 value (属性值)。

    • 如果 replacer 函数返回一个值,该值将被序列化。
    • 如果返回 undefined,则该属性将被从 JSON 字符串中省略。
    const product = {
      name: "Widget",
      price: 100,
      stock: 50,
      manufacturer: "Acme Inc.",
      details: null,
      secretCode: "XYZ123" // 不希望序列化
    };
    
    const filteredProductJson = JSON.stringify(product, (key, value) => {
      if (key === 'secretCode') {
        return undefined; // 排除这个属性
      }
      if (typeof value === 'string' && value.includes('Inc.')) {
        return value.toUpperCase(); // 修改特定字符串
      }
      return value;
    });
    console.log(filteredProductJson);
    // {"name":"Widget","price":100,"stock":50,"manufacturer":"ACME INC.","details":null}
    
4.2.3 带有 space 参数

JSON.stringify() 可以接受第三个可选参数 spacespace 用于控制输出 JSON 字符串的缩进。它允许您使输出更具可读性。

  • 如果 space 是一个数字,它表示每个缩进级别的空格数量 (0-10)。
  • 如果 space 是一个字符串,它将作为缩进字符。
const data = { a: 1, b: { c: 2, d: 3 } };

// 不缩进 (默认)
console.log(JSON.stringify(data)); // {"a":1,"b":{"c":2,"d":3}}

// 缩进 2 个空格
console.log(JSON.stringify(data, null, 2));
/*
{
  "a": 1,
  "b": {
    "c": 2,
    "d": 3
  }
}
*/

// 使用制表符缩进
console.log(JSON.stringify(data, null, '\t'));
/*
{
  "a": 1,
  "b": {
    "c": 2,
    "d": 3
  }
}
*/
4.2.4 JSON.stringify() 的行为和限制
  • 非对象/数组的值: 如果序列化的值不是对象或数组,它会被直接转换为 JSON 基本类型。
    console.log(JSON.stringify(123));          // "123" (字符串形式的数字)
    console.log(JSON.stringify("hello"));       // "\"hello\"" (双引号包裹的字符串)
    console.log(JSON.stringify(true));          // "true"
    console.log(JSON.stringify(null));          // "null"
    
  • 循环引用: 如果对象中存在循环引用,JSON.stringify() 会抛出 TypeError: Converting circular structure to JSON
  • 特殊值处理:
    • undefined、函数、Symbol 作为对象属性值时,这些属性会被跳过。作为数组元素时,它们会被转换为 null
    • 日期对象: 会被转换为 ISO 格式的字符串。
    • BigInt 会抛出 TypeError

5. JSON 的常见应用场景

5.1 Web API 数据传输

这是 JSON 最常见的用途。当 Web 浏览器(客户端)请求服务器(例如通过 fetchXMLHttpRequest)数据时,服务器通常会以 JSON 格式返回数据。客户端接收到 JSON 字符串后,使用 JSON.parse() 将其转换为 JavaScript 对象进行处理。反之,当客户端向服务器发送数据时,也会将 JavaScript 对象转换为 JSON 字符串 (JSON.stringify()) 发送。

// 客户端发送数据
const userData = { name: "John", age: 30 };
fetch('/api/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(userData) // 将 JS 对象转换为 JSON 字符串发送
})
.then(response => response.json()) // 接收 JSON 响应并解析
.then(data => console.log('Server response:', data))
.catch(error => console.error('Error:', error));
5.2 配置文件

许多现代工具和项目使用 JSON 文件来存储配置信息,因为其结构清晰且易于解析。例如,Node.js 项目中的 package.json 文件、VS Code 的 .vscode/settings.json 等。

// package.json 示例
{
  "name": "my-app",
  "version": "1.0.0",
  "description": "A simple Node.js application",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Your Name",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1"
  },
  "devDependencies": {
    "nodemon": "^2.0.7"
  }
}
5.3 本地存储 (LocalStorage/SessionStorage)

浏览器提供的 localStoragesessionStorage 只能存储字符串。因此,当需要存储 JavaScript 对象或数组时,必须先使用 JSON.stringify() 将其转换为 JSON 字符串,然后在读取时使用 JSON.parse() 转换回来。

const userSettings = {
  theme: "dark",
  fontSize: 16,
  notifications: true
};

// 存储对象
localStorage.setItem('userSettings', JSON.stringify(userSettings));
console.log('存储成功');

// 读取对象
const storedSettingsString = localStorage.getItem('userSettings');
const loadedSettings = JSON.parse(storedSettingsString);
console.log('加载的设置:', loadedSettings); // { theme: 'dark', fontSize: 16, notifications: true }

6. JSON 与 JavaScript 对象的区别

尽管 JSON 的语法来源于 JavaScript 对象字面量,但两者之间存在一些关键区别:

特征 JSON JavaScript 对象
用途 数据交换格式,独立于语言 JavaScript 语言内部的数据结构
键 (Keys) 必须是双引号包裹的字符串 ("key") 可以是字符串 (单引号/双引号/无引号)
值 (Values) 仅限于字符串、数字、布尔、null、对象、数组 除了 JSON 支持的类型,还可以是函数、undefinedDateSymbolBigInt
语法 严格,不允许逗号、注释等额外字符 相对宽松,允许尾部逗号、单引号、注释等
表现形式 总是字符串 内存中的一个数据结构

理解这些区别对于避免在处理 JSON 和 JavaScript 对象时产生混淆至关重要。


7. 总结

本教程对 JavaScript 中的 JSON 进行了全面深入的讲解,您现在应该掌握了:

  • JSON 的本质: 了解 JSON 是一种轻量级、自描述、语言无关的数据交换格式。
  • JSON 的严格语法: 掌握名称/值对、花括号、方括号和双引号包裹键的规则。
  • JSON 支持的数据类型: 明确哪些 JavaScript 数据类型可以安全地在 JSON 中表示。
  • 核心转换方法: 熟练使用 JSON.parse() 将 JSON 字符串转换为 JavaScript 对象,以及 JSON.stringify() 将 JavaScript 对象转换为 JSON 字符串。
  • 高级用法: 了解 reviverreplacerspace 参数如何提供更精细的控制。
  • 常见应用场景: 理解 JSON 在 Web API、配置文件和本地存储中的实际应用。
  • 与 JavaScript 对象的区别: 明确 JSON 和 JavaScript 对象之间的语法和类型限制。

JSON 是现代 Web 开发的基石,熟练运用它将极大地提高您处理数据和构建应用程序的能力。


8. 附录:术语表

  • JSON (JavaScript Object Notation): 一种轻量级、人类可读、易于机器解析的数据交换格式。
  • 名称/值对 (Name/Value Pair): JSON 数据的基本单元,由一个字符串键和一个值组成,形如 "key": value
  • JSON 对象 (JSON Object): 用花括号 {} 包裹的无序的名称/值对集合。
  • JSON 数组 (JSON Array): 用方括号 [] 包裹的有序值集合。
  • JSON.parse() JavaScript 内置方法,将 JSON 格式的字符串转换为 JavaScript 值或对象。
  • reviver 函数: JSON.parse() 的可选第二个参数,一个回调函数,用于在解析过程中对值进行自定义转换。
  • JSON.stringify() JavaScript 内置方法,将 JavaScript 值或对象转换为 JSON 格式的字符串。
  • replacer 参数: JSON.stringify() 的可选第二个参数,可以是一个数组或函数,用于过滤或修改将被序列化的属性。
  • space 参数: JSON.stringify() 的可选第三个参数,用于指定 JSON 输出字符串的缩进格式,以提高可读性。
  • 序列化 (Serialization): 将数据结构或对象状态转换为可以存储或传输的格式(通常是字符串)的过程。
  • 反序列化 (Deserialization) / 解析 (Parsing): 将序列化后的数据(字符串)转换回原始数据结构或对象的过程。
  • 回调地狱 (Callback Hell): 指深层嵌套的回调函数导致代码难以阅读、维护和调试的问题。
  • RESTful API: 一种网络应用程序接口设计风格,通常使用 HTTP 请求来获取或修改数据,并常以 JSON 格式交换数据。
  • LocalStorage/SessionStorage: 浏览器提供的 Web 存储 API,允许 Web 应用程序在客户端本地存储数据,但只能存储字符串。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐