一、JSON.stringify作用
将一个对象或值转换成一个JSON字符串。
console.log(JSON.stringify({ x: 5, y: 6 }));
// "{"x":5,"y":6}"
console.log(JSON.stringify(true));
// "true"
console.log(JSON.stringify(new Date()));
// 2020-03-01T13:42:52.105Z
console.log(JSON.stringify({ x: [10, undefined, function(){}, Symbol('')] }));
// "{"x":[10,null,null,null]}"
二、参数
有三个参数:
- value:要转换的数据
- replacer: 如果是函数,则在转换过程中每个属性的转换都要调用该函数;如果是数组则只转换数组中包含的属性,如果是null则转换所有属性。
- space:该参数用于美化json字符串,提高可读性。如果是数字表示空格的个数,最多10个,小于1表示没有空格;是字符串,则用字符串填充。
const data = {
a: 1,
b: {
c: 2,
}
}
const replacer = function(key, value) {
if (typeof value === 'number') {
return value * 2;
}
return value;
}
console.log(JSON.stringify(data, replacer, 4));
//"{
// "a": 2,
// "b": {
// "c": 4
// }
//}"
上面值为number类型时,都乘以2,同时前面填充4个空格。
const data = {
a: 1,
b: {
c: 2,
}
}
const replacer = function(key, value) {
if (typeof value === 'number') {
return value * 2;
}
return value;
}
console.log(JSON.stringify(data, replacer, "-"));
//"{
//-"a": 2,
//-"b": {
//--"c": 4
//-}
//}"
三、转换规则
-
如果数据中有toJSON方法,则使用toJSON返回的值。 比如Date对象就自带toJSON方法。
const data = { a: 1, b: 2, toJSON: function() { return this.a + this.b; } } // '3'
-
Boolean、Number和String都会转换成相应的原始值。
const data = { a: 1, b: '2', c: true, }; // '{"a":1,"b":"2","c":true}'
-
undefined、function、Symbol对于JSON都不是有效的值,所以都返回undefined;如果在对象中对应的属性将会被忽略;在数组中都转换成null;
JSON.stringify(undefined) // undefined JSON.stringify(function(){}) // undefined JSON.stringify(Symbol('foo')) // undefined const data = { a: undefined, b: function() {}, c: Symbol('foo'), d: [true, undefined, function(){}, Symbol.for('foo')], }; //'{"d":[true,null,null,null]}'
-
Symbol作为对象的key时,忽略该属性
const data = { a: 1, [Symbol.for('foo')]: 'baz' } //'{"a":1}'
-
Date自带toJSON方法,返回的值跟toISOString相同。
-
Infinity、NaN和null都会被认为是null。
const data = { a: 1, b: Infinity, c: NaN, d: null, }; // '{"a":1,"b":null,"c":null,"d":null}'
-
其他对象(Map、Set、WeakSet、WeakMap等)都会序列化其可枚举属性。
const data = { a: 1, }; Object.defineProperty(data, 'b', { value: 2, enumerable: false, configurable: true, }); // '{"a":1}' JSON.stringify(new Set([1])); // '{}' // 使用Object.keys(new Set([1])),获取不到任何属性
-
BigInt类型不能序列化,会报类型错误
JSON.stringify({x: 1n}); //Uncaught TypeError: Do not know how to serialize a BigInt
四、手写JSON.stringify
将以前自己写的实现方式整理了一下,代码如下:
function stringify(data, replacer = null, space = '') {
const toString = Object.prototype.toString;
const types = {
'[object String]': 'string',
'[object Number]': 'number',
'[object Undefined]': 'undefined',
'[object Null]': 'null',
'[object Function]': 'function',
'[object Array]': 'array',
'[object Boolean]': 'bool',
'[object Symbol]': 'symbol',
'[object BigInt]': 'bigInt',
'[object Object]': 'object',
'[object RegExp]': 'regexp',
'[object Date]': 'date',
'[object Set]': 'set',
'[object Map]': 'map',
};
/**
*
* @param {*} data
* 获取数据类型
*/
const getType = data => {
return types[toString.call(data)];
}
/**
*
* @param {*} key
* @param {*} value
*
* 处理stringify的replacer
* 如果是函数,直接调用函数,
* 如果是数组:没有key时或者包含key时返回数据本身,否者返回undefined;
*/
const replacerCallback = function (key = '', value) {
let type = getType(replacer);
switch (type) {
case 'function':
return replacer(key, value);
case 'array':
return ('' === key || replacer.includes(key)) ?
value :
undefined;
default:
return value;
}
}
/**
* 获取填充字符串
*/
const getSpaceStr = function () {
if (getType(space) === 'number') {
return Array(space).fill(' ').join('');
}
if (getType(space) === 'string') {
return space;
}
return '';
}
// 初始化填充字符串;
const spaceStr = getSpaceStr();
const needBeautify = spaceStr.length > 0 ? true : false;
// 获取填充的字符串
const getPadStr = (level) => "\n" + Array(level).fill(spaceStr).join('');
// 格式化字符串
const formatResult = function (data, level = 0) {
if (!needBeautify || 0 === level) {
return data;
}
return getPadStr(level) + data;
}
/**
*
* @param {*} data
* @param {*} level
* 格式化对象
*/
const formatObject = function (data, level = 0) {
if (data.length === 0) {
return formatResult('{}', level);
}
return formatResult('{', level) +
data.map(item => formatResult(item, level + 1))
+ ((needBeautify && level === 0) ? "\n" : '') +
formatResult('}', level);
}
/**
*
* @param {*} data : 数据本身
* @param {*} level: 数据所在的嵌套层数
* 格式化数组
*/
const formArray = function(data, level = 0) {
if (data.length === 0) {
return formatResult('[]', level);
}
const res = ('['
+ data.map(item => formatResult(item, level + 1))
+ ((needBeautify && level === 0) ? "\n" : '') +
formatResult(']', level));
const reg = new RegExp(getPadStr(level + 1) + getPadStr(level + 1), 'g');
const newStr = getPadStr(level + 1);
return res.replace(reg, newStr);
}
/**
*
* @param {*} key : 数据的键
* @param {*} origin : 数据的值
* @param {*} level : 数据在原始数据中的嵌套层数
*/
const translate = function (key, origin, level) {
let data = replacerCallback(key, origin);
switch (getType(data)) {
case 'bool':
return `${data}`;
case 'string':
return `"${data}"`;
case 'number':
if (data == Infinity || isNaN(data)) {
return 'null';
}
return `${data}`;
case 'undefined':
case 'function':
case 'symbol':
return 'undefined';
case 'null':
return 'null';
case 'bigInt':
throw new TypeError('BigInt can not seriallied');
case 'array':
const result = data.map((item, index) => {
let res = translate('', item, level + 1);
switch (res) {
case 'undefined':
return 'null';
default:
return res;
}
});
return formArray(result, level);
default:
if ('toJSON' in data) {
return data.toJSON();
}
let res = [];
for (let [key, value] of Object.entries(data)) {
if ('symbol' === getType(key)) {
continue;
}
let tmp = translate(key, value, level + 1);
if ('undefined' === tmp) {
continue;
}
// 对象需要格式化时,value前面有一个空格
res.push(`"${key}":${needBeautify ? ' ' : ''}${tmp}`);
}
return formatObject(res, level);
}
}
return translate('', data, 0);
}
// 测试数据
const data = {
a: 2,
b: {
y: 'y',
},
c: Symbol('foo'),
[Symbol.for('foo')]: 'd',
e: function(){},
x: [
{
y: 'y',
a: '3'
},
{
p: 'p',
q: 'q',
},
undefined,
null,
NaN,
"foo",
new Set([1]),
new WeakSet(),
new Map([{a: 1}]),
new WeakMap([[{a:1}, 2]]),
function(){},
],
};
const replacer = function (key, value) {
if (typeof value === 'number') {
return 2 * value;
}
return value;
}
console.log(stringify(data));
//'{"a":2,"b":{"y":"y"},"x":[{"y":"y","a":"3"},{"p":"p","q":"q"},null,null,null,"foo",{},{},{},{},null]}'
console.log(stringify(data, replacer));
//'{"a":4,"b":{"y":"y"},"x":[{"y":"y","a":"3"},{"p":"p","q":"q"},null,null,null,"foo",{},{},{},{},null]}'
console.log(stringify(data, ['a', 'x']));
// '{"a":2,"x":[{"a":"3"},{},null,null,null,"foo",{},{},{},{},null]}'
console.log(stringify(data, ['a', 'x'], '--'));
// '{
//--"a": 2,
//--"x": [
//----{
//------"a": "3"
//----},
//----{},
//----null,
//----null,
//----null,
//----"foo",
//----{},
//----{},
//----{},
//----{},
//----null
//--]
//}'
以上是自己写的实现stringify的全部代码,如果代码有不对的地方希望指出。
源码