手写JavaScript对象转JSON方法:从原理到实现
在JavaScript开发中,将对象转换为JSON字符串是一个常见的需求,虽然现代JavaScript提供了内置的JSON.stringify()方法来处理这个任务,但理解其底层原理并手写一个转换函数,不仅能加深对JSON序列化过程的理解,还能帮助我们处理一些特殊情况,本文将详细介绍如何手写一个将JavaScript对象转换为JSON字符串的函数。
JSON序列化的基本原理
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它基于JavaScript的一个子集,将JavaScript对象转换为JSON字符串需要遵循以下规则:
-
基本类型处理:
- 数字、布尔值直接转换
- 字符串需要用双引号包围
- null转换为"null"
- undefined会被忽略或转换为"undefined"(取决于实现)
-
对象处理:
- 属性名必须用双引号包围
- 属性值按上述规则转换
- 属性顺序可能不保留(ES6后规范要求保留)
-
数组处理:
- 元素按上述规则转换
- 保持数组顺序
-
其他情况:
- 循环引用需要处理(避免无限递归)
- 函数、Symbol等特殊类型通常会被忽略
手写JSON序列化函数的实现
下面我们逐步实现一个功能完善的stringify函数:
基础版本
function stringify(obj) {
// 处理基本类型
if (obj === null) {
return 'null';
}
if (typeof obj === 'string') {
return `"${obj}"`;
}
if (typeof obj === 'number' || typeof obj === 'boolean') {
return String(obj);
}
// 处理数组
if (Array.isArray(obj)) {
const elements = obj.map(item => stringify(item));
return `[${elements.join(',')}]`;
}
// 处理对象
if (typeof obj === 'object') {
const keys = Object.keys(obj);
const keyValuePairs = keys.map(key => {
const value = stringify(obj[key]);
// 忽略undefined值
if (value !== undefined) {
return `"${key}":${value}`;
}
return undefined;
}).filter(pair => pair !== undefined);
return `{${keyValuePairs.join(',')}}`;
}
// 其他类型(如function、symbol)返回undefined
return undefined;
}
增强版本(处理循环引用和更多选项)
function stringify(obj, replacer, space, seen = new WeakMap()) {
// 处理循环引用
if (seen.has(obj)) {
return '[Circular]';
}
// 处理基本类型
if (obj === null) {
return 'null';
}
if (typeof obj === 'string') {
return `"${obj.replace(/["\\]/g, '\\$&').replace(/\n/g, '\\n')}"`;
}
if (typeof obj === 'number' || typeof obj === 'boolean') {
return String(obj);
}
// 处理数组
if (Array.isArray(obj)) {
seen.set(obj, true);
const elements = obj.map(item => stringify(item, replacer, space, seen));
seen.delete(obj);
return `[${elements.join(',')}]`;
}
// 处理对象
if (typeof obj === 'object') {
seen.set(obj, true);
// 处理replacer函数
let processedObj = obj;
if (typeof replacer === 'function') {
processedObj = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const value = replacer.call(obj, key, obj[key]);
if (value !== undefined) {
processedObj[key] = value;
}
}
}
} else {
processedObj = obj;
}
const keys = typeof replacer === 'function'
? Object.keys(processedObj)
: (Array.isArray(replacer) ? replacer : Object.keys(processedObj));
const keyValuePairs = keys.map(key => {
const value = stringify(processedObj[key], replacer, space, seen);
// 忽略undefined值
if (value !== undefined) {
return `"${key}":${value}`;
}
return undefined;
}).filter(pair => pair !== undefined);
seen.delete(obj);
return `{${keyValuePairs.join(',')}}`;
}
// 其他类型(如function、symbol)返回undefined
return undefined;
}
完整版本(支持缩进格式化)
function stringify(obj, replacer, space, seen = new WeakMap()) {
// 处理循环引用
if (seen.has(obj)) {
return '[Circular]';
}
// 处理基本类型
if (obj === null) {
return 'null';
}
if (typeof obj === 'string') {
return `"${obj.replace(/["\\]/g, '\\$&').replace(/\n/g, '\\n')}"`;
}
if (typeof obj === 'number' || typeof obj === 'boolean') {
return String(obj);
}
// 处理space参数
let indent = '';
let newline = '';
if (typeof space === 'number') {
if (space > 0) {
indent = ' '.repeat(space);
newline = '\n';
}
} else if (typeof space === 'string') {
if (space.length > 0) {
indent = space;
newline = '\n';
}
}
// 处理数组
if (Array.isArray(obj)) {
seen.set(obj, true);
if (obj.length === 0) {
return '[]';
}
const elements = obj.map(item => {
const str = stringify(item, replacer, space, seen);
return newline + indent + str;
});
seen.delete(obj);
return `[${newline}${indent}${elements.join(`,${newline}${indent}`)}${newline}]`;
}
// 处理对象
if (typeof obj === 'object') {
seen.set(obj, true);
// 处理replacer函数
let processedObj = obj;
if (typeof replacer === 'function') {
processedObj = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const value = replacer.call(obj, key, obj[key]);
if (value !== undefined) {
processedObj[key] = value;
}
}
}
} else {
processedObj = obj;
}
const keys = typeof replacer === 'function'
? Object.keys(processedObj)
: (Array.isArray(replacer) ? replacer : Object.keys(processedObj));
if (keys.length === 0) {
return '{}';
}
const keyValuePairs = keys.map(key => {
const value = stringify(processedObj[key], replacer, space, seen);
// 忽略undefined值
if (value !== undefined) {
return `"${key}":${newline}${indent}${value}`;
}
return undefined;
}).filter(pair => pair !== undefined);
seen.delete(obj);
return `{${newline}${indent}${keyValuePairs.join(`,${newline}${indent}`)}${newline}}`;
}
// 其他类型(如function、symbol)返回undefined
return undefined;
}
使用示例
const obj = {
name: "John",
age: 30,
isStudent: false,
courses: ["Math", "Science"],
address: {
city: "New York",
zip: "10001"
},
sayHello: function() {
console.log("Hello");
},
[Symbol('id')]: 123
};
console.log(stringify(obj));
// 输出: {"name":"John","age":30,"isStudent":false,"courses":["Math","Science"],"address":{"city":"New York","zip":"10001"}}
// 带缩进的格式化
console.log(stringify(obj, null, 2));
/*
输出:
{
"name": "John",
"age": 30,
"isStudent": false,
"courses": [
"Math",
"Science"
],
"address": {
"city": "New York",
"zip": "10001"
}
}
*/
// 使用replacer函数
console.log(stringify(obj, (key, value) => {
if (typeof value === 'string') {
return value.toUpperCase();
}
return value;
}));
// 输出: {"name":"JOHN","age":30,"isStudent":false,"courses":["MATH","SCIENCE"],"address":{"city":"NEW YORK","zip":"10001"}}
与原生JSON.stringify()的区别
虽然我们手写的stringify函数实现了基本功能,但与原生的JSON.stringify()相比,仍有一些差异:
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
新浪足球直播
新浪足球直播
足球直播
足球直播
快连VPN
快连官网
足球直播
足球直播
快连VPN
快连官网
Google Chrome
Google Chrome
快连VPN
letsVPN
chrome浏览器
谷歌浏览器
足球直播
足球直播
欧易平台
欧易平台
欧易下载
欧易平台
欧易下载
欧易平台
欧易下载
欧易下载
欧易
欧易下载
欧易APP
欧易下载
欧易APP
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
欧易app
欧易app
欧易
欧易
NBA直播
足球直播
NBA直播
nba直播
英超直播
篮球直播
西甲直播
德甲直播



还没有评论,来说两句吧...