JSON(JavaScript Object Notation)是轻量级的数据交换格式。它基于 JavaScript 对象语法,但独立于语言,被广泛用于前后端数据传输。
🎯 JSON 语法#
基本规则#
// JSON 是纯文本格式const jsonString = '{"name": "张三", "age": 25}'
// 合法的 JSON 值类型:// - 字符串(必须用双引号)// - 数字// - 布尔值(true/false)// - null// - 数组// - 对象
// ✅ 合法 JSON;('{"name": "张三"}');('[1, 2, 3]');('"hello"');('123');('true');('null')
// 🔶 不合法 JSON;("{'name': '张三'}") // 单引号不行;('{name: "张三"}') // 键必须用双引号;('undefined') // undefined 不是 JSON 值;('{a: function(){}}') // 函数不是 JSON 值与 JavaScript 对象的区别#
// JavaScript 对象const jsObject = { 'name': '张三', // 键不需要引号 'my-age': 25, // 特殊键需要引号 'sayHi'() {}, // 可以有方法 'date': new Date(), // 可以有 Date 对象 'regex': /abc/, // 可以有正则 'undefined': undefined,}
// JSON 字符串const jsonString = `{ "name": "张三", "my-age": 25}`// 不能有方法、Date、正则、undefinedJSON.parse#
基本用法#
// 将 JSON 字符串解析为 JavaScript 值const jsonString = '{"name": "张三", "age": 25}'const obj = JSON.parse(jsonString)
console.log(obj.name) // '张三'console.log(obj.age) // 25
// 解析数组const arrString = '[1, 2, 3]'const arr = JSON.parse(arrString)console.log(arr) // [1, 2, 3]
// 解析基本类型JSON.parse('"hello"') // 'hello'JSON.parse('123') // 123JSON.parse('true') // trueJSON.parse('null') // null错误处理#
// 🔶 解析无效 JSON 会抛出错误try { JSON.parse('invalid json')} catch (error) { console.log(error.message) // Unexpected token...}
// 安全的解析���数function safeJsonParse(str, defaultValue = null) { try { return JSON.parse(str) } catch { return defaultValue }}
safeJsonParse('{"valid": true}') // { valid: true }safeJsonParse('invalid') // nullsafeJsonParse('invalid', {}) // {}reviver 函数#
// 第二个参数可以转换解析结果const jsonString = '{"name": "张三", "birthDate": "1990-05-20"}'
const obj = JSON.parse(jsonString, (key, value) => { // key 是属性名,value 是属性值 if (key === 'birthDate') { return new Date(value) // 转为 Date 对象 } return value})
console.log(obj.birthDate instanceof Date) // true
// 过滤属性const filtered = JSON.parse('{"a": 1, "b": 2, "c": 3}', (key, value) => { if (key === 'b') return undefined // 排除 b return value})console.log(filtered) // { a: 1, c: 3 }JSON.stringify#
基本用法#
// 将 JavaScript 值转为 JSON 字符串const obj = { name: '张三', age: 25 }const jsonString = JSON.stringify(obj)
console.log(jsonString) // '{"name":"张三","age":25}'
// 转换数组JSON.stringify([1, 2, 3]) // '[1,2,3]'
// 转换基本类型JSON.stringify('hello') // '"hello"'JSON.stringify(123) // '123'JSON.stringify(true) // 'true'JSON.stringify(null) // 'null'特殊值处理#
// undefined、函数、Symbol 会被忽略或转为 nullconst obj = { a: undefined, b: function () {}, c: Symbol('sym'), d: 'valid',}
JSON.stringify(obj) // '{"d":"valid"}'
// 在数组中会转为 nullJSON.stringify([undefined, function () {}, Symbol()])// '[null,null,null]'
// NaN 和 Infinity 转为 nullJSON.stringify(NaN) // 'null'JSON.stringify(Infinity) // 'null'
// Date 转为 ISO 字符串JSON.stringify(new Date()) // '"2025-01-15T02:30:00.000Z"'
// 正则转为空对象JSON.stringify(/abc/) // '{}'replacer 参数#
// 数组形式:只包含指定属性const obj = { a: 1, b: 2, c: 3 }JSON.stringify(obj, ['a', 'b']) // '{"a":1,"b":2}'
// 函数形式:自定义转换const data = { name: '张三', password: '123456', age: 25,}
const result = JSON.stringify(data, (key, value) => { if (key === 'password') { return undefined // 排除密码 } if (typeof value === 'number') { return value * 2 // 数字翻倍 } return value})console.log(result) // '{"name":"张三","age":50}'space 参数(格式化)#
const obj = { name: '张三', info: { age: 25, city: '北京' } }
// 使用 2 个空格缩进console.log(JSON.stringify(obj, null, 2))/*{ "name": "张三", "info": { "age": 25, "city": "北京" }}*/
// 使用 tab 缩进console.log(JSON.stringify(obj, null, '\t'))
// 使用 4 个空格console.log(JSON.stringify(obj, null, 4))toJSON 方法#
// 对象可以定义 toJSON 方法自定义序列化const user = { name: '张三', password: '123456', toJSON() { return { name: this.name, // 不包含 password } },}
JSON.stringify(user) // '{"name":"张三"}'
// Date 内置了 toJSONconst date = new Date()date.toJSON() // '2025-01-15T02:30:00.000Z'深拷贝#
JSON 方式#
// 使用 JSON 实现深拷贝function deepClone(obj) { return JSON.parse(JSON.stringify(obj))}
const original = { name: '张三', info: { age: 25 },}
const cloned = deepClone(original)cloned.info.age = 30
console.log(original.info.age) // 25(原对象未变)console.log(cloned.info.age) // 30🔶 JSON 深拷贝的限制#
const obj = { date: new Date(), // 变成字符串 regex: /abc/, // 变成空对象 func: function () {}, // 丢失 undef: undefined, // 丢失 symbol: Symbol('sym'), // 丢失 nan: NaN, // 变成 null infinity: Infinity, // 变成 null circular: null, // 循环引用会报错}
// obj.circular = obj // 循环引用// JSON.stringify(obj) // TypeError: Converting circular structure to JSON
const cloned = JSON.parse(JSON.stringify(obj))console.log(cloned)// {// date: '2025-01-15T02:30:00.000Z',// regex: {},// nan: null,// infinity: null// }更完善的深拷贝#
function deepClone(obj, hash = new WeakMap()) { // 处理 null 和非对象 if (obj === null || typeof obj !== 'object') { return obj }
// 处理 Date if (obj instanceof Date) { return new Date(obj) }
// 处理 RegExp if (obj instanceof RegExp) { return new RegExp(obj) }
// 处理循环引用 if (hash.has(obj)) { return hash.get(obj) }
// 处理数组和对象 const cloned = Array.isArray(obj) ? [] : {} hash.set(obj, cloned)
for (const key in obj) { if (obj.hasOwnProperty(key)) { cloned[key] = deepClone(obj[key], hash) } }
return cloned}
// 现代方法:structuredClone(浏览器和 Node.js 17+)const cloned = structuredClone(obj)实际应用#
本地存储#
// 存储对象到 localStorageconst user = { name: '张三', age: 25, preferences: { theme: 'dark' } }
// 存储localStorage.setItem('user', JSON.stringify(user))
// 读取const stored = localStorage.getItem('user')const parsedUser = stored ? JSON.parse(stored) : null
// 封装const storage = { set(key, value) { localStorage.setItem(key, JSON.stringify(value)) }, get(key, defaultValue = null) { const item = localStorage.getItem(key) try { return item ? JSON.parse(item) : defaultValue } catch { return defaultValue } }, remove(key) { localStorage.removeItem(key) },}
storage.set('config', { theme: 'dark' })storage.get('config') // { theme: 'dark' }API 数据处理#
// 发送 JSON 数据async function postData(url, data) { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(data), })
return response.json() // 自动解析 JSON}
// 处理响应async function fetchUser(id) { const response = await fetch(`/api/users/${id}`) const data = await response.json()
// 转换日期字段 return { ...data, createdAt: new Date(data.createdAt), updatedAt: new Date(data.updatedAt), }}日志和调试#
// 格式化输出对象function prettyPrint(obj) { console.log(JSON.stringify(obj, null, 2))}
// 带时间戳的日志function log(message, data) { console.log( JSON.stringify( { timestamp: new Date().toISOString(), message, data, }, null, 2 ) )}
log('��户登录', { userId: 123, ip: '192.168.1.1' })比较对象#
// 简单对象比较(有局限性)function simpleEqual(obj1, obj2) { return JSON.stringify(obj1) === JSON.stringify(obj2)}
simpleEqual({ a: 1, b: 2 }, { a: 1, b: 2 }) // true
// 🔶 注意:属性顺序会影响结果simpleEqual({ a: 1, b: 2 }, { b: 2, a: 1 }) // false
// 改进版:排序键function sortedStringify(obj) { return JSON.stringify(obj, Object.keys(obj).sort())}
// 更好的方式:深度比较函数function deepEqual(a, b) { if (a === b) return true if (typeof a !== 'object' || typeof b !== 'object') return false if (a === null || b === null) return false
const keysA = Object.keys(a) const keysB = Object.keys(b)
if (keysA.length !== keysB.length) return false
return keysA.every((key) => deepEqual(a[key], b[key]))}配置文件处理#
// 合并配置function mergeConfig(defaults, custom) { // 深拷贝默认配置 const config = JSON.parse(JSON.stringify(defaults))
// 递归合并 function merge(target, source) { for (const key in source) { if ( source[key] && typeof source[key] === 'object' && !Array.isArray(source[key]) ) { target[key] = target[key] || {} merge(target[key], source[key]) } else { target[key] = source[key] } } return target }
return merge(config, custom)}
const defaults = { theme: 'light', language: 'zh', features: { darkMode: false, animations: true },}
const custom = { theme: 'dark', features: { darkMode: true },}
mergeConfig(defaults, custom)// { theme: 'dark', language: 'zh', features: { darkMode: true, animations: true } }性能优化#
大数据处理#
// 🔶 大对象序列化可能阻塞主线程const hugeArray = new Array(100000).fill({ data: 'some data' })
// 考虑使用 Web Workerconst worker = new Worker('json-worker.js')worker.postMessage({ action: 'stringify', data: hugeArray })worker.onmessage = (e) => { const jsonString = e.data}
// 或分批处理async function stringifyLargeArray(arr, batchSize = 1000) { const chunks = [] for (let i = 0; i < arr.length; i += batchSize) { chunks.push(JSON.stringify(arr.slice(i, i + batchSize))) await new Promise((resolve) => setTimeout(resolve, 0)) // 让出主线程 } return '[' + chunks.map((c) => c.slice(1, -1)).join(',') + ']'}选择性序列化#
// 只序列化需要的字段const user = { id: 1, name: '张三', password: 'secret', profile: { /* 大量数据 */ }, logs: [ /* 更多数据 */ ],}
// 使用 replacer 数组JSON.stringify(user, ['id', 'name']) // '{"id":1,"name":"张三"}'总结#
| 方法 | 说明 |
|---|---|
JSON.parse(str) | 解析 JSON 字符串 |
JSON.parse(str, reviver) | 带转换函数的解析 |
JSON.stringify(obj) | 转为 JSON 字符串 |
JSON.stringify(obj, replacer) | 带过滤的序列化 |
JSON.stringify(obj, null, space) | 格式化输出 |
| 类型 | stringify 结果 |
|---|---|
undefined | 被忽略/null |
function | 被忽略/null |
Symbol | 被忽略/null |
Date | ISO 字符串 |
RegExp | 空对象 {} |
NaN/Infinity | null |
核心要点:
- JSON 键必须用双引号
- 不支持 undefined、函数、Symbol、循环引用
- 用 try-catch 包裹 parse 防止错误
- 深拷贝有限制,复杂场景用 structuredClone
- 大数据处理注意性能问题