文章目录
-
- [JSON 的由来](#JSON 的由来)
- [JSON 的基本语法](#JSON 的基本语法)
- [JSON 的序列化](#JSON 的序列化)
-
- 简单使用
- [stringify 方法之 replacer](#stringify 方法之 replacer)
- [stringify 方法之 replacer 参数传入回调函数](#stringify 方法之 replacer 参数传入回调函数)
- [stringify 方法之 space](#stringify 方法之 space)
- [stringify 方法之 toJSON](#stringify 方法之 toJSON)
- [parse 方法之 reviver](#parse 方法之 reviver)
- [利用 stringify 和 parse 实现深拷贝](#利用 stringify 和 parse 实现深拷贝)
json 相信大家一定耳熟能详,但是其实 json 在 JavaScript 中很多细节,本文就来详细讲解 json 在 JavaScript 中那些隐藏的秘密
JSON 的由来
- JSON 是一种数据格式 ,而不是编程语言,目前广泛用于客户端和服务器之间传输的数据格式
- JSON 全称 JavaScript Object Notation(JavaScript 对象符号)
- JSON 是由Douglas Crockford构想和设计的一种轻量级资料交换格式,算是 JavaScript 的一个子集
- 虽然 JSON 被提出来的时候是主要应用 JavaScript 中,但是目前已经独立于编程语言,可以在各个编程语言中被广泛的使用
JSON 的基本语法
- JSON 支持三种类型的值:
- 简单值:数字、字符串(不支持单引号)、布尔类型、null 类型
- 对象值:由key、value组成,key是字符串类型,并且必须添加双引号,值可以是简单值、对象值、数组值
- 数组值:数组的值可以是简单值、对象值、数组值
tips
:JSON 里面不能添加注释- 具体的写法大家应该都知道,这里就不写示例代码了
JSON 的序列化
相信这个大家都知道,在 JavaScript 中的 JSON 对象上存在两个方法 stringify 和 parse,就可以实现序列化
序列化是将数据结构或对象转换为符合 JSON 格式的字符串的过程
简单使用
js
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
-
stringify 方法的第一个参数大家都知道,那第二个参数(
replacer
)了解吗? -
replacer 是一个可选参数,replacer 的作用是指定需要想转换的值,是一个数组,或 null(全部转换) ,如下:
jsconst 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
参数中强制指定包含了它们
stringify 方法之 replacer 参数传入回调函数
-
上述例子中可以一定程度上实现一些额外的需求,但是针对高度自定义的需求还是有点无能为力,那么此时我们可以给其参数改为传入一个回调函数
-
传入一个回调函数时,会接收两个参数,key 和 value,而每一次进行序列转换的时候就会执行一次,如下:
jsconst 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,如下:
jsconst 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)
-
结果如图:
stringify 方法之 space
-
此方法除了第二个参数还存在第三个参数(
space
),此参数的作用为指定缩进用的空白字符串,用于美化输出 -
当 space 是一个数字时:它代表有多少的空格;上限为 10; 该值若小于 1,则意味着没有空格,如下:
jsconst 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 个字符),如下:
jsconst 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 时,如图:
stringify 方法之 toJSON
如果一个被序列化的对象拥有
toJSON
方法,那么该toJSON
方法就会覆盖该对象默认的序列化行为:不是该对象被序列化,而是调用toJSON
方法后的返回值会被序列化
-
这个方法还是很好理解的,就是原来是转换原对象,但是如果有这个方法的话就会以我们这个方法的返回值为基准,如下:
jsconst obj = { name: '张三', age: 18, friends: [ { name: '李四', age: 20 }, { name: '王五', age: 22 } ], hobbies: ['篮球', '足球', '乒乓球'], toJSON: function () { // 返回数字 return 111 } } console.log('toJSON: ', JSON.stringify(obj))
-
结果如图:
-
同样,可以更换为其他对象,如下:
jsconst 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))
-
结果如图:
parse 方法之 reviver
-
reviver 也是一个函数,执行时机在在 parse 函数返回之前,所以也可也来进行拦截或者说修改解析前的原始值
-
使用如下:
jsconst 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 实现深拷贝
-
stringify 和 parse 搭配使用的时候,可以完成深拷贝,不敢存在一些限制,比如函数,循环引用,symbol 等等一些清空都是无法使用这种方式完成深拷贝的
-
只需要先使用 stringify 方法将对象转为 JSON 字符串,在使用 parse 方法进行解析即可,配合使用如下:
jsconst 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 形式的数据的对象时,使用此方法是一种非常不错的选择