javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > js实现JSON.stringify

js实现JSON.stringify的方法实例代码

作者:zhengzhengwang

在大厂的前端面试过程中,JSON.Stringify常常被问到,那么能够熟练的运用,掌握就必不可少,下面这篇文章主要介绍了js实现JSON.stringify的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

实现一个自定义的 JSON.stringify 函数,并详细解释其实现原理和边界情况处理。

1. 基础版本实现

function myJSONStringify(data) {
    // 处理基本类型
    if (typeof data === 'string') {
        return `"${data}"`;
    }
    
    if (typeof data === 'number' || typeof data === 'boolean') {
        return String(data);
    }
    
    if (data === null) {
        return 'null';
    }
    
    if (data === undefined || typeof data === 'function' || typeof data === 'symbol') {
        return undefined; // 这些类型在对象中会被忽略,在数组中会转为null
    }
    
    // 处理数组
    if (Array.isArray(data)) {
        const arrValues = data.map(item => {
            const value = myJSONStringify(item);
            return value === undefined ? 'null' : value;
        });
        return `[${arrValues.join(',')}]`;
    }
    
    // 处理普通对象
    if (typeof data === 'object') {
        // 处理循环引用
        if (hasCircularReference(data)) {
            throw new TypeError('Converting circular structure to JSON');
        }
        
        const keys = Object.keys(data);
        const objValues = keys.map(key => {
            const value = myJSONStringify(data[key]);
            // 忽略值为 undefined、函数、symbol 的属性
            if (value === undefined) {
                return null;
            }
            return `"${key}":${value}`;
        }).filter(item => item !== null);
        
        return `{${objValues.join(',')}}`;
    }
}

2. 完整的增强版本

class JSONStringify {
    constructor() {
        this.cache = new Set(); // 用于检测循环引用
    }
    
    stringify(data) {
        this.cache.clear();
        return this._stringify(data);
    }
    
    _stringify(data) {
        // 处理基本类型
        if (typeof data === 'string') {
            return this._escapeString(data);
        }
        
        if (typeof data === 'number') {
            // 处理特殊数值
            if (Number.isNaN(data) || !Number.isFinite(data)) {
                return 'null';
            }
            return String(data);
        }
        
        if (typeof data === 'boolean') {
            return String(data);
        }
        
        if (data === null) {
            return 'null';
        }
        
        if (data === undefined || typeof data === 'function' || typeof data === 'symbol') {
            return undefined;
        }
        
        // 处理 BigInt (JSON.stringify 会报错)
        if (typeof data === 'bigint') {
            throw new TypeError('Do not know how to serialize a BigInt');
        }
        
        // 检查循环引用
        if (this.cache.has(data)) {
            throw new TypeError('Converting circular structure to JSON');
        }
        this.cache.add(data);
        
        // 处理 Date 对象
        if (data instanceof Date) {
            return `"${data.toISOString()}"`;
        }
        
        // 处理 RegExp 对象
        if (data instanceof RegExp) {
            return '{}';
        }
        
        // 处理数组
        if (Array.isArray(data)) {
            const result = this._stringifyArray(data);
            this.cache.delete(data);
            return result;
        }
        
        // 处理普通对象
        if (typeof data === 'object') {
            const result = this._stringifyObject(data);
            this.cache.delete(data);
            return result;
        }
    }
    
    _stringifyArray(arr) {
        const values = arr.map((item, index) => {
            // 处理数组中的空位
            if (index in arr) {
                const value = this._stringify(item);
                return value === undefined ? 'null' : value;
            }
            return 'null'; // 数组空位转为 null
        });
        
        return `[${values.join(',')}]`;
    }
    
    _stringifyObject(obj) {
        const keys = Object.keys(obj);
        const pairs = [];
        
        for (const key of keys) {
            // 处理 Symbol 键(JSON.stringify 会忽略 Symbol 键)
            if (typeof key === 'symbol') {
                continue;
            }
            
            const value = this._stringify(obj[key]);
            
            // 忽略值为 undefined、function、symbol 的属性
            if (value !== undefined) {
                pairs.push(`"${this._escapeString(key)}":${value}`);
            }
        }
        
        // 处理 toJSON 方法
        if (obj.toJSON && typeof obj.toJSON === 'function') {
            const jsonValue = obj.toJSON();
            if (jsonValue !== undefined) {
                return this._stringify(jsonValue);
            }
        }
        
        return `{${pairs.join(',')}}`;
    }
    
    _escapeString(str) {
        // 处理特殊字符
        const escapeMap = {
            '"': '\\"',
            '\\': '\\\\',
            '\b': '\\b',
            '\f': '\\f',
            '\n': '\\n',
            '\r': '\\r',
            '\t': '\\t'
        };
        
        return str.replace(/["\\\b\f\n\r\t]/g, match => escapeMap[match]);
    }
}

// 循环引用检测函数
function hasCircularReference(obj, seen = new Set()) {
    if (obj === null || typeof obj !== 'object') {
        return false;
    }
    
    if (seen.has(obj)) {
        return true;
    }
    
    seen.add(obj);
    
    if (Array.isArray(obj)) {
        for (const item of obj) {
            if (hasCircularReference(item, seen)) {
                return true;
            }
        }
    } else {
        for (const key in obj) {
            if (obj.hasOwnProperty(key)) {
                if (hasCircularReference(obj[key], seen)) {
                    return true;
                }
            }
        }
    }
    
    seen.delete(obj);
    return false;
}

3. 处理 replacer 参数

function myJSONStringifyWithReplacer(data, replacer, space) {
    const jsonStringify = new JSONStringify();
    
    // 处理 replacer 参数
    if (replacer) {
        if (typeof replacer === 'function') {
            return _stringifyWithFunctionReplacer(data, replacer, space);
        } else if (Array.isArray(replacer)) {
            return _stringifyWithArrayReplacer(data, replacer, space);
        }
    }
    
    // 处理 space 参数
    const result = jsonStringify.stringify(data);
    if (space !== undefined) {
        return _formatWithSpace(result, space);
    }
    
    return result;
}

function _stringifyWithFunctionReplacer(data, replacer, space) {
    const seen = new Set();
    
    function stringify(value, key) {
        // 处理循环引用
        if (value !== null && typeof value === 'object') {
            if (seen.has(value)) {
                throw new TypeError('Converting circular structure to JSON');
            }
            seen.add(value);
        }
        
        // 应用 replacer 函数
        const replacedValue = replacer.call(data, key, value);
        
        let result;
        if (typeof replacedValue === 'string') {
            result = `"${replacedValue}"`;
        } else if (typeof replacedValue === 'number' || typeof replacedValue === 'boolean') {
            result = String(replacedValue);
        } else if (replacedValue === null) {
            result = 'null';
        } else if (Array.isArray(replacedValue)) {
            result = '[' + replacedValue.map((item, index) => 
                stringify(item, String(index))).join(',') + ']';
        } else if (typeof replacedValue === 'object') {
            const keys = Object.keys(replacedValue);
            const pairs = keys.map(k => {
                const val = stringify(replacedValue[k], k);
                return `"${k}":${val}`;
            });
            result = '{' + pairs.join(',') + '}';
        } else {
            result = undefined;
        }
        
        if (value !== null && typeof value === 'object') {
            seen.delete(value);
        }
        
        return result;
    }
    
    return stringify(data, '');
}

function _stringifyWithArrayReplacer(data, replacer, space) {
    const replacerSet = new Set(replacer);
    
    function filterObject(obj) {
        if (Array.isArray(obj)) {
            return obj.map(item => 
                item !== null && typeof item === 'object' ? filterObject(item) : item
            );
        }
        
        if (obj !== null && typeof obj === 'object') {
            const filtered = {};
            for (const key in obj) {
                if (replacerSet.has(key)) {
                    filtered[key] = obj[key];
                }
            }
            return filtered;
        }
        
        return obj;
    }
    
    const filteredData = filterObject(data);
    const jsonStringify = new JSONStringify();
    const result = jsonStringify.stringify(filteredData);
    
    if (space !== undefined) {
        return _formatWithSpace(result, space);
    }
    
    return result;
}

4. 处理 space 参数(格式化)

function _formatWithSpace(jsonStr, space) {
    if (space === 0) return jsonStr;
    
    let indentLevel = 0;
    let formatted = '';
    let inString = false;
    
    // 确定缩进字符串
    const indentStr = typeof space === 'number' 
        ? ' '.repeat(Math.min(space, 10))
        : space.slice(0, 10);
    
    for (let i = 0; i < jsonStr.length; i++) {
        const char = jsonStr[i];
        
        if (char === '"' && jsonStr[i - 1] !== '\\') {
            inString = !inString;
            formatted += char;
            continue;
        }
        
        if (inString) {
            formatted += char;
            continue;
        }
        
        switch (char) {
            case '{':
            case '[':
                formatted += char + '\n' + indentStr.repeat(++indentLevel);
                break;
                
            case '}':
            case ']':
                formatted += '\n' + indentStr.repeat(--indentLevel) + char;
                break;
                
            case ',':
                formatted += ',\n' + indentStr.repeat(indentLevel);
                break;
                
            case ':':
                formatted += ': ';
                break;
                
            default:
                formatted += char;
        }
    }
    
    return formatted;
}

5. 使用示例

// 测试数据
const testData = {
    name: "张三",
    age: 25,
    isStudent: true,
    hobbies: ["读书", "游泳", null],
    address: {
        city: "北京",
        district: "朝阳区"
    },
    birthday: new Date('2000-01-01'),
    empty: null,
    undef: undefined,      // 会被忽略
    func: function() {},   // 会被忽略
    [Symbol('id')]: 123    // 会被忽略
};

// 创建循环引用
testData.self = testData;

// 测试
try {
    console.log("普通对象:", myJSONStringify(testData));
} catch (e) {
    console.log("循环引用错误:", e.message);
}

// 移除循环引用后测试
delete testData.self;

console.log("普通对象:", myJSONStringify(testData));
console.log("原生 JSON.stringify:", JSON.stringify(testData));

// 测试 replacer 函数
const jsonWithReplacer = myJSONStringifyWithReplacer(
    testData,
    (key, value) => {
        if (key === 'password') return undefined;
        if (typeof value === 'string') return value.toUpperCase();
        return value;
    }
);
console.log("带 replacer:", jsonWithReplacer);

// 测试格式化输出
console.log("格式化输出:", myJSONStringifyWithReplacer(testData, null, 2));

6. 特性对比

特性原生 JSON.stringify我们的实现
基本类型转换
数组处理
对象处理
循环引用检测
Date 对象
RegExp 对象{}{}
toJSON 方法
replacer 函数
replacer 数组
space 格式化
Symbol 键忽略忽略
undefined 值忽略/转为 null忽略/转为 null
BigInt报错报错

这个实现涵盖了 JSON.stringify 的主要特性,包括:

总结 

到此这篇关于js实现JSON.stringify的文章就介绍到这了,更多相关js实现JSON.stringify内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文