持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情
我们在日常开发中,JSON对象可谓是经常使用,比如使用ajax时需要对请求或返回的数据格式进行转换,比如在对一些简单数据类型的对象做深拷贝时……
众所周知,JSON对象一共有两个方法:JSON.stringify()和JSON.parse()。它们的功能正好相反,前者是将对象转为JSON格式的字符串,而后者是将JSON格式的字符串转为对象。那么JSON.stringify()是如何实现的呢?
JSON.stringify的参数
它有3个参数:
- 第一个参数是要转换的数据
- 第二个参数是可选的,它可以是函数,也可以是数组;
- 如果是函数则在序列化过程中,被序列化的值的每个属性都会经过该函数的转换和处理;
- 如果该参数是一个数组,则只有包含在这个数组中的属性名才会被序列化到最终的 JSON 字符串中;
- 如果该参数为 null 或者未提供,则对象所有的属性都会被序列化。
- 第三个参数也是可选的,它是为了美化输出的,如果参数是数字,它代表前面有多少个空格
案列:
let data = [{name:'ly',age:18,sex:'man'},{name:'zs',age:20,sex:'man'},{name:'lili',age:22,sex:'woman'},{name:'lx',age:30,sex:'man'}]
let res = JSON.stringify(data,(key,value)=>{
if(key==='age') {
return value*2;
}
return value;
})
console.log(res);
我们发现age都变成两倍了,如果是数组的话:
let data = [{name:'ly',age:18,sex:'man'},{name:'zs',age:20,sex:'man'},{name:'lili',age:22,sex:'woman'},{name:'lx',age:30,sex:'man'}]
let res = JSON.stringify(data,['name'])
console.log(res);
那么他就只序列化name属性了。
第三个参数的作用就是为了美化代码:我们之前的例子发现输出都不太好看,那么第三个参数就起作用了.我们只需要在第三个参数传入一个数字,假如我们传入4:
let data = [{name:'ly',age:18,sex:'man'},{name:'zs',age:20,sex:'man'},{name:'lili',age:22,sex:'woman'},{name:'lx',age:30,sex:'man'}]
let res = JSON.stringify(data,(key,value)=>{
if(key==='age') {
return value*2;
}
return value;
},4)
console.log(res);
这里使用了 4 个空格做为层级缩进。
搞清楚转换规则
做任何事情都要遵守一定的规则,而JSON.stringify的规则就是:
-
基本数据类型
- undefined和symbol类型输出:undefined
- number类型输出:字符串类型的number
- NaN和Infinity输出:"null"
- null输出:"null"
- string类型输出:string类型
- boolean类型输出:字符串类型的true/false
-
引用数据类型:
- RegExp类型:输出"{}"
- Date类型:Date的toString字符串值
- 数组中出现了undefined、function和symbol类型:输出null
- 普通对象类型:
- 如果有toJSON方法,那么序列化toJSON()的返回值
- 如果出现了任意函数、undefined和symbol值,则忽略
- 所有以symbol为属性键的属性都为被完全忽略
所以如果是复杂类型的数据,使用JSON.stringify进行深拷贝是不可取的。
手写实现
现在我们熟悉了JSON.stringify的用法和规则之后,我们就可以自己手动去实现实现这个方法了:
function jsonStringify(data) {
let type = typeof data;
if(type !== 'object' && type !== 'function') {
let result;
if(type === 'symbol' || type === 'undefined') {
result = "undefined";
} else if(Number.isNaN(data) || data === Infinity) {
result = "null"
} else {
result = String(data);
}
return result;
} else if(type ==='function') {
return "undefined";
} else {
if(data === null) {
return "null"
} else if(data.toJSON&&typeof data.toJSON === 'function') {
return jsonStringify(data.toJSON())
} else if(data instanceof RegExp) {
return "{}"
} else if(data instanceof Date) {
return data.toJSON()
} else if(Array.isArray(data)) {
let result = [];
data.forEach((item,index)=>{
result[index] = jsonStringify(item);
})
return result = "["+result+"]";
} else {
let result = [];
Object.keys(data).forEach((key,index)=>{
if(typeof key !== 'symbol') {
if(data[key] !== 'undefined' && data[key] !== 'symbol' && data[key] !== 'function') {
result.push(""+key+""+":"+jsonStringify(data[key]));
}
}
})
return ("{"+result+"}").replace(/'/g,'"')
}
}
}
let data = [{name:'ly',age:18,sex:'man'},{name:'zs',age:20,sex:'man'},{name:'lili',age:22,sex:'woman'},{name:'lx',age:30,sex:'man'}]
let res = jsonStringify(data)
console.log(res);
我们发现现在基本已经可以了,唯一的缺点就是key和value为啥没有双引号,等我后面再看看看,或者有没有大佬来解答解答......