JSON.stringify()基本使用与注意点

364 阅读4分钟

JSON.stringify()基本使用与注意点

语法

JSON.stringfy(value[, replacer [, space]]) 

参数说明

  • value:必需, 要转换的 JavaScript 值(通常为对象或数组)。
  • replacer: 可选
  1. 用于转换结果的函数或数组;
  2. 如果 replacer 为函数,则 JSON.stringify 将调用该函数,并传入每个成员的键和值。使用返回值而不是原始值;
  3. 如果此函数返回 undefined,则排除成员。根对象的键是一个空字符串:""。如果 replacer 是一个数组,则仅转换该数组中具有键值的成员。成员的转换顺序与键在数组中的顺序一样。
  • space:可选,文本添加缩进、空格和换行符,如果 space 是一个数字,则返回值文本在每个级别缩进指定数目的空格,如果 space 大于 10,则文本缩进 10 个空格。space 也可以使用非数字,使输出的格式更美化。

返回值

返回包含 JSON 文本的字符串。

基本使用

  1. JSON.stringify可以转换基本类型值(String,Boolean,Number)或引用类型对象(Object)数组(Array),我们平时使用最多的也是转换对象或数组;
  2. 可以指定replacer为函数选择性的地替换;
  3. 也可通过指定replacer为数组指定替换;
    // 转换字符串
    console.log(JSON.stringfy('Rylan')); // "Rylan"
    // 转换数值
    console.log(JSON.stringfy(18)); // "18"
    // 转换布尔值
    console.log(JSON.stringfy(true)); // "true"
    // 转换对象
    console.log(JSON.stringfy({name:'Rylan',age:18})); // "{'name':'Rylan','age':'18'}"
    // 转换数组
    console.log(JSON.stringfy(['Rylan',18])); // "['Rylan','18']"
    // replacer函数选择性替换
    console.log(JSON.stringfy({name:'Rylan',age:18},(key,value)=>{
        return typeof age==='number'? value:undefined
    })); // "{'name':'Rylan','age':'18'}"
    //replacer指定数组 
    console.log(JSON.stringify({ name: 'Rylan', sex: 'male', age: 18 }, [ 'name' ]))  //'{"name":"Rylan"}'
    

使用注意点

    一开始使用JSON.stringfy没有多去注意应该注意的点,导致很多问题,
    开始认识的时候只想到能使用就行,查看官网发现居然有这么多特性......

特性一

布尔值数字字符串的包装对象在序列化过程中会自动转换成对应的原始值;

    console.log(JSON.stringfy('Rylan',18,true)) //"'Rylan','18','true'"

特性二

  1. 在对象中, undefined任意函数symbol值在转换中会被忽略;
  2. 在数组中, undefined任意函数symbol值会转换为null;
  3. undefined任意函数symbol值单独转换会被直接转换为undefined;
    // 对象中函数会被忽略
    console.log(JSON.stringfy({
        name:"Rylan",
        age:18,
        // 函数会被忽略
        logMyName(){
            console.log("my name is Rylan")
        },
        //undefined会被忽略
        sex:undefined,
        // Symbol会被忽略 
        symbolName: Symbol('前端胖头鱼')
    }))
    //"{'name':'Rylan,'age':'18'}"
    
    //数组中存在着三种值会被转化为null
    console.log(JSON.stringfy([
    'Rylan',
    18,
    logMyName(){
        console.log("my name is Rylan")
    },
    undefined,
    Symbol("Rylan")
    ])
    //"['Rylan','18',null,null,null]"
    
    //单独转换为undefined
    console.log(JSON.stringify( function logMyName () { 
        console.log('my name is Rylan') 
    } )) // undefined 
    console.log(JSON.stringify(undefined)) // undefined 
    console.log(JSON.stringify(Symbol('Rylan'))) // undefined

特性三

NaNInfinity 格式的数值及null 都会被当做 null;

    console.log(JSON.stringify({ age: NaN, age2: Infinity, name: null }))
    //"{'age': null, 'age2': null, 'name': null}"

特性四

转换BigInt类型的值时会抛出错误;

    const bigNumber = BigInt(1000000000000000n);
    //呜噜噜...,这个都不知道这么读>>>>10个100万亿n~~~
    console.log(JSON.stringify(bigNumber));
    // TypeError: Do not know how to serialize a BigInt

特性五

转换值包含toJSON()方法,定义转换的值将被序列化;

    const objToJSON = { 
        name: 'Rylan', 
        toJSON () { 
            return 'MY NAME IS RYLAN' 
        } 
    }
    console.log(JSON.stringify(objToJSON.toJSON));
    //'MY NAME IS RYLAN' 

特性六

日期函数new Date()调用toJSON方法转换为字符串,也会被当做字符串处理;

    let date=new Date();
    //转换为字符串
    console.log(date.toJSON()) //2021-12-01T00:00:00.932Z
    console.log(JSON.stringify(date)) // "2021-12-01T00:00:00.932Z"

特性七

对包含循环利用,即对象之间相互引用的关系,形成无限循环,会抛出错误;

    let loopObj = { name: 'Rylan', };
    //引用关系导致无限赋值
    loopObj.obj=loopObj;
    console.log(JSON.stringify(loopObj)) 
    // Converting circular structure to JSON

特性八

对于Map/Set/WeakMap/WeakSet此类型的对象,仅序列化其可枚举的属性;

    let enumableObj={};
    Object.defineProperties(enumerableObj, { 
        name: { value: 'Rylan', enumerable: true }, 
        age: { value: 18, enumerable: false }, 
    })
    console.log(JSON.stringify(enumerableObj));
    // '{"name":"Rylan"}'

特性九

对象中,以Symbol值作为键名会被直接忽略,就算使用replacer函数参数中重新定义值;

    console.log(JSON.stringify({ [Symbol('Rylan')]: 'Rylan'} ));
    // {}
    console.log(JSON.stringify({ 
        [ Symbol('Rylan') ]: 'Rylan', }, 
        (key, value) => { 
        if (typeof key === 'symbol') 
        { return value } 
    }))
    // undefined

自己实现一个简单的JSON.stringify

    function myJSONstringify(params) {
        // console.log(params)

        let type=typeof params;
        // 用于获取完整的数据类型
        function getType(data){
            // 获取完整的数据类型
            let type=Object.prototype.toString.call(data);
            // 正则匹配并转换为小写
            let result=type.replace(/\[object (.*?)\]/, '$1').toLowerCase()
                return result 
        }
        function isLoop(obj){
            let bool=false;
            let map=new Map();
            function findLoop(obj){
                Object.values(obj).forEach((value)=>{
                    if(typeof value==='object'){
                        if(map.has(value)){
                            return bool=true
                        }else{
                            map.set(value);
                            findLoop(value)
                        }
                    }
                })
            }
            findLoop(obj)
            return bool
        }
            
        // 判断是否是bigint类型,则抛出错误
        if(type==='bigint'){
            throw new TypeError('Do not know how to serialize a BigInt')
        }
        // 普通类型或传入值为null
        if(type!=='object'||params===null){    
            // 非对象此三种特殊键会返回会null
            let specialName=[NaN, Infinity, null];
            // 非对象此三种基本的数据类型会返回undefined
            let baseType=['undefined','function','symbol']
            if(specialName.includes(params)){
                // NaN 和 Infinity 格式的数值及null 都会被当做 null
                return ""+null+""
            }else if(baseType.includes(params)){
               // undefined、任意函数、symbol值单独转换会被直接转换为undefined
                return undefined
            }else if(type==='string'){
                // 布尔值、数字、字符串的包装对象在序列化过程中会自动转换成对应的原始值
                return "'"+params+"'"
            }else if(type==='number'||type==='boolean'){
                // 布尔值、数字、字符串的包装对象在序列化过程中会自动转换成对应的原始值
                return ""+params+""
            }
        }else{
            let baseType=['undefined','function','symbol']
            // 判断是数组
            if(Array.isArray(params)){
                // console.log(params);
                let resultData=params.map(item=>{  

                    if(baseType.includes(typeof item)){
                        // 在数组中, undefined、任意函数、symbol值会转换为null
                        return ""+null+""
                    }else{
                        // 递归调用,判断数组里面是否是对象或数组
                        return myJSONstringify(item)
                    }
                })
                return "["+resultData+"]"
            }else{
                // 对包含循环利用,即对象之间相互引用的关系,形成无限循环,会抛出错误;
                if(isLoop(params)){
                    throw new TypeError('Converting circular structure to JSON')
                }
                // 转换值包含toJSON()方法,定义转换的值将被序列化
                if (typeof params.toJSON === 'function') {
                    return myJSONstringify(params.toJSON())
                }    
                let baseType=['undefined','function','symbol'];
                let simpleType=['number','boolean']    
                if (simpleType.includes(getType(params))) {
                    return ""+params+""
                } else if (getType(params) === 'string') {
                    return '"' + params + '"'
                } else {
                    let resultData = [];
                    Object.keys(params).forEach((key) => {
                        //对象中,以Symbol值作为键名会被直接忽略,就算使用replacer函数参数中重新定义值;
                        if (typeof key !== 'symbol') {
                            const value = params[key]    
                            // 在对象中, undefined、任意函数、symbol值在转换中会被忽略
                            if (!baseType.includes(typeof value)) {
                                resultData.push(`"${key}":${myJSONstringify(value)}`)
                            }
                        }
                    })
                    // console.log(resultData)
                    return `{${resultData}}`.replace(/'/, '"')
                }
            }
        }

    }

结尾

天啦了噜~,脑瓜子嗡嗡响的;以上就是对JSON.stringify的认识,便于方便记忆,以及实现,若有不足欢迎大家提出~~