1. JSON.stringify
1.1 基本概念
JSON.stringify
方法是将一个 JavaScript 对象或值转换为 JSON 字符串,默认该方法其实有三个参数:第一个参数是必选,后面两个是可选参数非必选。第一个参数传入的是要转换的对象;第二个是一个 replacer 函数,比如指定的 replacer 是数组,则可选择性地仅处理包含数组指定的属性;第三个参数用来控制结果字符串里面的间距
JSON.stringify({ x: [10, undefined, function () { }, Symbol('')] }) // '{"x":[10,null,null,null]}'
/* 第二个参数的例子 */
function replacer(key, value) {
if (typeof value === "string") {
return undefined;
}
return value;
}
var foo = { foundation: "Mozilla", model: "box", week: 4, transport: "car", month: 7 };
var jsonString = JSON.stringify(foo, replacer);
console.log(jsonString); // '{"week":4,"month":7}'
/* 第三个参数的例子 */
JSON.stringify({ a: 2 }, null, " ");
/*'{
"a": 2
}'*/
JSON.stringify({ a: 2 }, null, ""); // '{"a":2}'
1.2 分析各种数据类型及边界情况
JSON.stringify | 输入 | 输出 |
---|---|---|
基础数据类型 | undefined | undefined |
boolean | 'false' / 'true' | |
number | 字符串类型的数值 | |
symbol | undefined | |
null | 'null' | |
string | string | |
NaN 和 Infinity | "null" | |
引用数据类型 | function | undefined |
Array数组中出现了 undefined、function以及symbol | string / "null" | |
RegExp | "{}" | |
Date | Date的 toJSON()字符串 | |
普通 object | 1. 如果有 toJSON() 方法,那么序列化 toJSON()的返回值<br />2. 如果属性值中出现了 undefined、任意的函数以及 symbol值,忽略<br />3. 所有以 symbol为属性键的属性都会被完全忽略掉 |
还有一个特殊情况需要注意:对于包含循环引用的对象(深拷贝那讲中也有提到)执行此方法,会抛出错误
1.3 代码逻辑实现
先利用 typeof 把基础数据类型和引用数据类型分开,分开之后再根据不同情况来分别处理不同的情况
function jsonStringify(data) {
return typeof data !== 'object'
? isNotObject(data)
: isObject(data)
}
function isNotObject(data) {
let type = typeof data;
switch (true) {
// NaN 和 Infinity 序列化返回 "null"
case Number.isNaN(data):
case data === Infinity:
case data === -Infinity:
return "null"
// 由于 function 序列化返回 undefined,因此和 undefined、symbol 一起处理
case type === 'function':
case type === 'undefined':
case type === 'symbol':
return undefined
case type === 'string':
return `"${data}"`
default:
return String(data);
}
}
function isObject(data) {
switch (true) {
case data === null:
return "null";
case data.toJSON:
case typeof data.toJSON === 'function':
return jsonStringify(data.toJSON());
case data instanceof Array:
let resultArr = [];
// 如果是数组,那么数组里面的每一项类型又有可能是多样的
data.forEach((item, index) => {
resultArr[index] = (typeof item === 'undefined' || typeof item === 'function' || typeof item === 'symbol')
? "null"
: jsonStringify(item);
});
return `[${resultArr}]`.replace(/'/g, '"');
default:
// 处理普通对象
let result = [];
Object.keys(data).forEach((item, index) => {
if (typeof item !== 'symbol') {
// key 如果是 symbol 对象,忽略
if (data[item] !== undefined && typeof data[item] !== 'function' && typeof data[item] !== 'symbol') {
// 键值如果是 undefined、function、symbol,忽略
result.push(`"${item}":${jsonStringify(data[item])}`)
}
}
});
return `{${result}}`.replace(/'/g, '"');
}
}
- 由于 function 返回 'null', 并且
typeof function
能直接返回精确的判断,故在整体逻辑处理基础数据类型的时候,随着 undefined,symbol 直接处理了 - 由于
typeof null
的时候返回'object'
,故 null 的判断逻辑整体在处理引用数据类型的逻辑里面 - 关于引用数据类型中的数组,由于数组的每一项的数据类型又有很多的可能性,故在处理数组过程中又将 undefined,symbol,function 作为数组其中一项的情况做了特殊处理
- 同样在最后处理普通对象的时候,key (键值)也存在和数组一样的问题,故又需要再针对上面这几种情况(undefined,symbol,function)做特殊处理;
- 最后在处理普通对象过程中,对于循环引用的问题暂未做检测,如果是有循环引用的情况,需要抛出 Error;
- 根据官方给出的 JSON.stringify 的第二个以及第三个参数的实现,本段模拟实现的代码并未实现
1.4 测试
let nl = null;
console.log(jsonStringify(nl) === JSON.stringify(nl)); // true
let und = undefined;
console.log(jsonStringify(undefined) === JSON.stringify(undefined)); // true
let boo = false;
console.log(jsonStringify(boo) === JSON.stringify(boo)); // true
let nan = NaN;
console.log(jsonStringify(nan) === JSON.stringify(nan));
// true
let inf = Infinity;
console.log(jsonStringify(Infinity) === JSON.stringify(Infinity)); // true
let str = "jack";
console.log(jsonStringify(str) === JSON.stringify(str)); // true
let reg = new RegExp("\w");
console.log(jsonStringify(reg) === JSON.stringify(reg)); // true
let date = new Date();
console.log(jsonStringify(date) === JSON.stringify(date)); // true
let sym = Symbol(1);
console.log(jsonStringify(sym) === JSON.stringify(sym)); // true
let array = [1, 2, 3];
console.log(jsonStringify(array) === JSON.stringify(array)); // true
let obj = {
name: 'jack',
age: 18,
attr: ['coding', 123],
date: new Date(),
uni: Symbol(2),
sayHi: function () {
console.log("hi")
},
info: {
sister: 'lily',
age: 16,
intro: {
money: undefined,
job: null
}
}
}
console.log(jsonStringify(obj) === JSON.stringify(obj)) // true
2. JSON.parse
JSON.parse
方法用来解析 JSON 字符串,构造由字符串描述的 JavaScript 值或对象。该方法有两个参数:第一个参数是需要解析处理的 JSON 字符串,第二个参数是可选参数提供可选的 reviver 函数,用在返回之前对所得到的对象执行变换操作
const json = '{"result":true, "count":2}';
const obj = JSON.parse(json);
console.log(obj.count); // 2
console.log(obj.result); // true
/* 带第二个参数的情况 */
JSON.parse('{"p": 5}', function (k, v) {
if (k === '') return v;
return v * 2;
}); // { p: 10 }
网友评论