先来了解一下几个概念:
JSON 格式
- JSON 是一种轻量级的、基于文本的、与语言无关的语法,用于定义数据交换格式
- 它来源于 ECMAScript 编程语言,但是独立于编程语言
对象字面量
- 是创建对象的一种快捷方式,英文名:object literal
- 对应还有函数字面量、数组字面量等等
- 字面量的性能优于使用 new 创建
JSON 特征
- JSON 就是一串字符串,使用特定的符号标注
- {} 双括号表示对象
- [] 中括号表示数组
- “” 双引号内是属性键或值
JSON 键
- 只能是字符串
- 必须用双引号包裹
JSON 值
- object
- array
- number
- string
- true
- false
- null
举几个合格 JSON 格式的例子:
`["大", "家", "好"]`
`{ "name": "Jae", "age": 23 }`
`{ "Arr": ["1", "2", "3"] }`
`{ "name": null }`
`{}`
`[]`
再看几个不合格 JSON 格式的例子:
`{
"name": "Jae",
[Symbol.for("sex")]: 23 // 错误,键只能是字符串,不能是 Symbol
}`
`{
name: "Jae", // 键必须用双引号包裹,此处没有引号
'age': 23 // 键必须用双引号包裹,此处用的是单引号
}`
`[-10, 0xDDFF]` // 只允许十进制,这里用了十六进制
`{
"name": "car",
"created": new Date(), // 不能有 Date
"price": 100,
"getPrice": function() { // 不能有 function
return this.price
}
}`
`{
"name": "Jae",
"age": 23, // 最后不能有逗号
}`
JSON.parse()
把 JSON 字符串转化为方便使用的对象,这个方法相信大家已经很熟悉了。
这里介绍一下大部分人会忽略的用法,JSON.parse()
的第二个参数:
JSON.parse() 第二个参数 reviver(k, v)
第二个参数为一个函数 reviver(k, v)
,k 表示当前需要转换的属性键,v 表示当前需要转换的属性值;如果该函数返回 undefined,当前属性会从对象中删除;如果返回其他值,就会成为一个有效值。下面来看看具体的代码示例:
var jsonStr = `{
"name": "Jae",
"age": 23,
"sex": "boy",
"IdCard": "350204199903256985"
}`
// 隐藏身份证信息
var obj = JSON.parse(jsonStr, function(key, value) {
if (key === 'IdCard') {
return undefined;
} else {
return value;
}
});
console.log(obj); // { name: 'Jae', age: 23, sex: 'boy' }
JSON.stringify()
把对象或者基础的值转化为 JSON 字符串的方法,它同样有几个大家容易忽略的参数:
- 语法:
JSON.stringify(value[, replacer [, space]])
- 第二个参数
replacer
:过滤属性或者处理值 - 第三个参数
space
:美化输出格式
JSON.stringify() 第二个参数 replacer
- 如果该参数是一个函数:则在序列化过程中,被序列化的值的每个属性都会经过该函数的转换和出处理
- 如果该参数是一个数组:则只有包含在这个数组中的属性名才会被序列化到最终的 JSON 字符串中
- 如果该参数为 null 或者未提供:则对象所有属性都会被序列化
看一个例子:
var person = {
name: "Jae",
age: 23,
sex: "boy",
IdCard: "350204199903256985"
};
var jsonString = JSON.stringify(person, function(key, value) {
if (typeof value === "string") {
return undefined
} else {
return value
}
});
console.log(jsonString); // '{"age":23}'
console.log(JSON.stringify(person, ['name', 'age'])); // '{"name":"Jae","age":23}'
JSON.stringify() 第三个参数 space
- 如果参数是数字:它代表有多少的空格;上限为10。该值若小于1,则意味着没有空格
- 如果该参数为字符串:当字符串长度超过10个字母,取其前10个字母,该字符串将被作为空格
- 如果该参数为 null 或者未提供:没有空格
看一个例子:
var person = {
name: "Jae",
age: 23,
sex: "boy",
IdCard: "350204199903256985"
};
console.log(person); // '{"name":"Jae","age":23,"sex":"boy","IdCard":"350204199903256985"}'
console.log(JSON.stringify(person, null, 4));
// 输出为:
// {
// "name":"Jae",
// "age":23,
// "sex":"boy",
// "IdCard":"350204199903256985"
// }
JSON.stringify() 其他规则
- 若值为 undefined、函数、Symbol
- 作为对象属性值,自动忽略
- 作为数组,序列号返回 null
- 单独序列化时,返回 undefined
- Date 返回 ISO 字符串
- 循环引用报错
- NaN、Infinity、null 返回 null
- BigInt 报错
- Map/Set/WeakMap 等对象,仅序列化可枚举属性
具体看一下代码:
// 作为对象属性值
const data = {
a: "test1",
b: undefined,
c: Symbol("test2"),
fn: function() {
return true;
}
}
console.log(JSON.stringify(data)); // '{"a":"test1"}'
// 作为数组
const data = ["test1", undefined, function aa() {return true}, Symbol("test2")];
console.log(JSON.stringify(data)); // '["test1", null, null, null]'
// 单独序列化时
const a1 = JSON.stringify(function fun() {return true})
const a2 = JSON.stringify(undefined)
const a3 = JSON.stringify(Symbol('test'))
console.log(a1, a2, a3); // undefined undefined undefined
// Date
console.log(JSON.stringify({now: new Date()})); // '{"now":"2022-03-30T06:02:17.348Z"}'
// NaN、Infinity、null
console.log(JSON.stringify(NaN)); // null
console.log(JSON.stringify(Infinity)); // null
console.log(JSON.stringify(null)); // null
// 转换为对应的原始值
console.log(JSON.stringify([new Number(2), new String("test"), new Boolean(false)])); // [2,"test",false]
// 仅序列化可枚举属性
const a = JSON.stringify(
Object.create(null, {
test1: { value: 'test1', enumerable: false },
test2: { value: 'test2', enumerable: true }
})
); // '{"test2":"test2"}'
// 循环引用报错
const obj = {
name: "Jae"
};
const obj2 = {
obj
};
// 对象之间形成循环引用,形成闭环
obj.obj2 = obj2
// 封装一个深拷贝函数
function deepClone(obj) {
return JSON.parse(JSON.stringify(obj));
}
// 执行深拷贝,抛出错误
deepClone(obj); // Uncaught TypeError: Converting circular structure to JSON
// BigInt 报错
console.log(JSON.stringify(10n)); // Uncaught TypeError: Do not know how to serialize a BigInt
toJSON
总的来说 JSON.stringify()
还是偏复杂的。如果对象拥有 toJSON
方法,toJSON
会覆盖对象默认的序列化行为。看一下代码:
let obj = {
"name": "Jae",
"age": 23,
"school": {
"address": "xxxxxx",
"phone": 13306006259
},
toJSON() {
return {
name: "Jae"
}
}
}
console.log(JSON.stringify(obj)); // {"name":"Jae"}