文章目录
- JSON 的由来
- JSON 的基本语法
- JSON 的序列化
- 简单使用
- stringify 方法之 replacer
- stringify 方法之 replacer 参数传入回调函数
- stringify 方法之 space
- stringify 方法之 toJSON
- parse 方法之 reviver
- 利用 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 格式的字符串的过程
简单使用
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(全部转换) ,如下:
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
参数中强制指定包含了它们
stringify 方法之 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)
-
结果如图:
stringify 方法之 space
-
此方法除了第二个参数还存在第三个参数(
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 时,如图:
stringify 方法之 toJSON
如果一个被序列化的对象拥有
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))
-
结果如图:
parse 方法之 reviver
-
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 实现深拷贝
-
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 形式的数据的对象时,使用此方法是一种非常不错的选择