json 相信大家一定耳熟能详,但是其实 json 在 JavaScript 中很多细节,本文就来详细讲解 json 在 JavaScript 中那些隐藏的秘密
tips
:JSON 里面不能添加注释相信这个大家都知道,在 JavaScript 中的 JSON 对象上存在两个方法 stringify 和 parse,就可以实现序列化
序列化是将数据结构或对象转换为符合 JSON 格式的字符串的过程
const obj = {
name: '张三',
age: 18,
frienfs: [
{ name: '李四', age: 20 },
{ name: '王五', age: 22 }
],
hobbies: ['篮球', '足球', '乒乓球']
}
// 使用 stringify 方法转为 JSON 格式的字符串
const str = JSON.stringify(obj)
// 使用 parse 方法将一个 JSON 格式的字符串转为一个对象
const newObj = JSON.parse(str)
stringify 方法的第一个参数大家都知道,那第二个参数(replacer
)了解吗?
replacer 是一个可选参数,replacer 的作用是指定需要想转换的值,是一个数组,或 null(全部转换) ,如下:
const obj = {
name: '张三',
age: 18,
friends: [
{ name: '李四', age: 20 },
{ name: '王五', age: 22 }
],
hobbies: ['篮球', '足球', '乒乓球']
}
// 正常转换
const str1 = JSON.stringify(obj)
console.log('str1: ', str1)
// 排除 friends 和 hobbies
const str2 = JSON.stringify(obj, ['name', 'age'])
console.log('str2: ', str2)
结果如图:
此时就可以发现,只转换了我们需要的部分,当我们存在这样的需求的时候,使用这个参数,是非常方便的
tips
:所有以 symbol 为属性键的属性都会被完全忽略掉,即便 replacer
参数中强制指定包含了它们
上述例子中可以一定程度上实现一些额外的需求,但是针对高度自定义的需求还是有点无能为力,那么此时我们可以给其参数改为传入一个回调函数
传入一个回调函数时,会接收两个参数,key 和 value,而每一次进行序列转换的时候就会执行一次,如下:
const obj = {
name: '张三',
age: 18,
friends: [
{ name: '李四', age: 20 },
{ name: '王五', age: 22 }
],
hobbies: ['篮球', '足球', '乒乓球']
}
const str = JSON.stringify(obj, (key, value) => {
console.log(key, value)
return value
})
console.log(str)
执行结果如图:
比如我们需要给 name 的值添加一个 @ 符号,并年龄 +1,如下:
const obj = {
name: '张三',
age: 18,
friends: [
{ name: '李四', age: 20 },
{ name: '王五', age: 22 }
],
hobbies: ['篮球', '足球', '乒乓球']
}
const str = JSON.stringify(obj, (key, value) => {
if (key === 'name') {
return value + '@'
}
if (key === 'age') {
return value + 1
}
return value
})
console.log(str)
结果如图:
此方法除了第二个参数还存在第三个参数(space
),此参数的作用为指定缩进用的空白字符串,用于美化输出
当 space 是一个数字时:它代表有多少的空格;上限为 10; 该值若小于 1,则意味着没有空格,如下:
const obj = {
name: '张三',
age: 18,
friends: [
{ name: '李四', age: 20 },
{ name: '王五', age: 22 }
],
hobbies: ['篮球', '足球', '乒乓球']
}
// 正常转换
const str1 = JSON.stringify(obj)
console.log('正常转换: ', str1)
// 2个空格
const str2 = JSON.stringify(obj, null, 2)
console.log('2个空格: ', str2)
结果如图:
此时输出的结果,就不是单纯的一行字符串,而是经过美化的格式,在一些调试查看数据的时候,还是非常好用的
当 space 是一个字符串时:则每一级别会比上一级别多缩进该字符串(或该字符串的前 10 个字符),如下:
const obj = {
name: '张三',
age: 18,
friends: [
{ name: '李四', age: 20 },
{ name: '王五', age: 22 }
],
hobbies: ['篮球', '足球', '乒乓球']
}
// 正常转换
const str1 = JSON.stringify(obj)
console.log('正常转换: ', str1)
// 空格时
const str2 = JSON.stringify(obj, null, ' ')
console.log('空格时: ', str2)
// 使用制表符(\t)来缩进
const str3 = JSON.stringify(obj, null, '\t')
console.log('制表符(\t): ', str3)
// 字符长度超出 10
const str4 = JSON.stringify(obj, null, 'aaabbbcccdddeee')
console.log('字符长度超出 10: ', str4)
正常转换时,如图:
空格时,如图:
制表符时,如图:
字符长度超出 10 时,如图:
如果一个被序列化的对象拥有
toJSON
方法,那么该toJSON
方法就会覆盖该对象默认的序列化行为:不是该对象被序列化,而是调用toJSON
方法后的返回值会被序列化
这个方法还是很好理解的,就是原来是转换原对象,但是如果有这个方法的话就会以我们这个方法的返回值为基准,如下:
const obj = {
name: '张三',
age: 18,
friends: [
{ name: '李四', age: 20 },
{ name: '王五', age: 22 }
],
hobbies: ['篮球', '足球', '乒乓球'],
toJSON: function () {
// 返回数字
return 111
}
}
console.log('toJSON: ', JSON.stringify(obj))
结果如图:
同样,可以更换为其他对象,如下:
const obj = {
name: '张三',
age: 18,
friends: [
{ name: '李四', age: 20 },
{ name: '王五', age: 22 }
],
hobbies: ['篮球', '足球', '乒乓球'],
toJSON: function () {
// 返回数字
return {
name: '李白',
descripton: '一个浪漫的人'
}
}
}
console.log('toJSON: ', JSON.stringify(obj, null, 2))
结果如图:
reviver 也是一个函数,执行时机在在 parse 函数返回之前,所以也可也来进行拦截或者说修改解析前的原始值
使用如下:
const str = `{"name":"张三","age":18,"friends":[{"name":"李四","age":20},{"name":"王五","age":22}],"hobbies":["篮球","足球","乒乓球"]}`
const obj = JSON.parse(str, (key, value) => {
if (key === 'age') {
return value + 2
}
return value
})
console.log(obj)
输出如图:
在生成的对象之后,我们也可以发现,年龄是发生了变化的
stringify 和 parse 搭配使用的时候,可以完成深拷贝,不敢存在一些限制,比如函数,循环引用,symbol 等等一些清空都是无法使用这种方式完成深拷贝的
只需要先使用 stringify 方法将对象转为 JSON 字符串,在使用 parse 方法进行解析即可,配合使用如下:
const obj = {
name: '张三',
age: 18,
friends: [
{ name: '李四', age: 20 },
{ name: '王五', age: 22 }
],
hobbies: ['篮球', '足球', '乒乓球']
}
const obj2 = JSON.parse(JSON.stringify(obj))
console.log('obj2: ', obj2)
console.log('修改 obj2 的 name 属性的值为田七')
obj2.name = '田七'
console.log('obj: ', obj)
console.log('obj2: ', obj2)
结果如图:
可以看到,obj 并没有因为修改了 obj2 的值而受到影响,在一些简单的 JSON 形式的数据的对象时,使用此方法是一种非常不错的选择