实现一个JSON.stringify
手写实现JSON.stringify方法,支持各种数据类型的序列化处理
问题
JSON.stringify 是 JavaScript 中用于将对象转换为 JSON 字符串的方法。需要实现一个自定义的 stringify 函数,能够正确处理各种数据类型,包括基本类型、对象、数组、特殊值等,并遵循 JSON.stringify 的转换规则。
解答
function myStringify(value, replacer = null, space = 0) {
// 处理缩进空格
let indent = '';
if (typeof space === 'number' && space > 0) {
indent = ' '.repeat(Math.min(space, 10));
} else if (typeof space === 'string') {
indent = space.slice(0, 10);
}
// 当前缩进级别
let currentIndent = '';
// 主序列化函数
function serialize(val, depth = 0) {
// 更新当前缩进
currentIndent = indent.repeat(depth);
const nextIndent = indent.repeat(depth + 1);
// 处理 null
if (val === null) {
return 'null';
}
// 处理布尔值
if (typeof val === 'boolean') {
return String(val);
}
// 处理数字
if (typeof val === 'number') {
// NaN 和 Infinity 转换为 null
return isFinite(val) ? String(val) : 'null';
}
// 处理字符串
if (typeof val === 'string') {
return `"${escapeString(val)}"`;
}
// 处理函数、undefined、Symbol - 在对象中会被忽略,在数组中转为 null
if (typeof val === 'function' || typeof val === 'undefined' || typeof val === 'symbol') {
return undefined;
}
// 处理 Date 对象
if (val instanceof Date) {
return `"${val.toISOString()}"`;
}
// 处理数组
if (Array.isArray(val)) {
const items = val.map(item => {
const serialized = serialize(item, depth + 1);
// undefined、函数、symbol 在数组中转为 null
return serialized === undefined ? 'null' : serialized;
});
if (indent && items.length > 0) {
return `[\n${nextIndent}${items.join(`,\n${nextIndent}`)}\n${currentIndent}]`;
}
return `[${items.join(',')}]`;
}
// 处理普通对象
if (typeof val === 'object') {
// 检测循环引用
if (seen.has(val)) {
throw new TypeError('Converting circular structure to JSON');
}
seen.add(val);
const keys = replacer && Array.isArray(replacer)
? replacer.filter(key => val.hasOwnProperty(key))
: Object.keys(val);
const pairs = [];
for (const key of keys) {
const value = val[key];
const serialized = serialize(value, depth + 1);
// 忽略 undefined、函数、symbol
if (serialized !== undefined) {
const keyStr = `"${escapeString(String(key))}"`;
pairs.push(indent ? `${nextIndent}${keyStr}: ${serialized}` : `${keyStr}:${serialized}`);
}
}
seen.delete(val);
if (indent && pairs.length > 0) {
return `{\n${pairs.join(',\n')}\n${currentIndent}}`;
}
return `{${pairs.join(',')}}`;
}
return undefined;
}
// 转义字符串中的特殊字符
function escapeString(str) {
const escapeMap = {
'"': '\\"',
'\\': '\\\\',
'\b': '\\b',
'\f': '\\f',
'\n': '\\n',
'\r': '\\r',
'\t': '\\t'
};
return str.replace(/["\\\b\f\n\r\t]/g, char => escapeMap[char]);
}
// 用于检测循环引用
const seen = new WeakSet();
// 处理 replacer 函数
if (typeof replacer === 'function') {
value = replacer('', value);
}
const result = serialize(value);
// 顶层的 undefined、函数、symbol 返回 undefined
return result === undefined ? undefined : result;
}
使用示例
// 基本类型
console.log(myStringify(123)); // "123"
console.log(myStringify('hello')); // "\"hello\""
console.log(myStringify(true)); // "true"
console.log(myStringify(null)); // "null"
// 特殊值
console.log(myStringify(undefined)); // undefined
console.log(myStringify(NaN)); // "null"
console.log(myStringify(Infinity)); // "null"
// 对象
const obj = {
name: 'John',
age: 30,
active: true,
score: null
};
console.log(myStringify(obj));
// {"name":"John","age":30,"active":true,"score":null}
// 数组
const arr = [1, 'test', true, null, undefined, NaN];
console.log(myStringify(arr));
// [1,"test",true,null,null,null]
// 嵌套对象
const nested = {
user: {
name: 'Alice',
hobbies: ['reading', 'coding']
},
count: 42
};
console.log(myStringify(nested));
// {"user":{"name":"Alice","hobbies":["reading","coding"]},"count":42}
// 使用缩进
console.log(myStringify(nested, null, 2));
// {
// "user": {
// "name": "Alice",
// "hobbies": [
// "reading",
// "coding"
// ]
// },
// "count": 42
// }
// Date 对象
console.log(myStringify(new Date('2024-01-01')));
// "2024-01-01T00:00:00.000Z"
// 忽略函数和 undefined
const withFunc = {
name: 'test',
fn: function() {},
undef: undefined,
value: 123
};
console.log(myStringify(withFunc));
// {"name":"test","value":123}
// 循环引用检测
const circular = { name: 'test' };
circular.self = circular;
try {
myStringify(circular);
} catch (e) {
console.log(e.message); // "Converting circular structure to JSON"
}
关键点
-
类型判断顺序:按照 null、boolean、number、string、function/undefined/symbol、Date、Array、Object 的顺序进行判断,确保特殊类型优先处理
-
特殊值处理:NaN 和 Infinity 转换为 null;undefined、函数、Symbol 在对象中被忽略,在数组中转为 null;顶层返回 undefined
-
字符串转义:正确转义双引号、反斜杠、换行符等特殊字符,确保生成的 JSON 字符串合法
-
循环引用检测:使用 WeakSet 记录已访问的对象,检测到循环引用时抛出 TypeError 异常
-
缩进格式化:支持 space 参数,可以是数字(空格数)或字符串,实现美化输出的 JSON 格式
-
Date 对象处理:Date 对象调用 toISOString() 方法转换为 ISO 8601 格式的字符串
-
replacer 支持:支持数组形式的 replacer,用于过滤对象的键(函数形式的 replacer 可进一步扩展)
-
递归序列化:通过递归处理嵌套的对象和数组,并正确管理缩进层级
目录