Basic knowledge of

The following information from MDN is relatively comprehensive and easy to understand

define

The json.stringify () method converts a JavaScript object or value to a JSON string, optionally replacing the value if a replacer function is specified, or optionally containing only the properties specified by the array if the specified replacer is an array.

grammar

JSON.stringify(value[, replacer [, space]])
Copy the code

parameter

value

The value to be serialized into a JSON string.

replacer optional

If the parameter is a function, each attribute of the serialized value is converted and processed by the function during serialization;

If the parameter is an array, only the property names contained in the array will be serialized into the final JSON string;

If this parameter is null or not provided, all attributes of the object are serialized.

space optional

Specifies a blank string for indentation, which is used to beautify the output (pretty-print).

If the argument is a number, it represents how many Spaces there are; The upper limit is 10. If the value is less than 1, there are no Spaces. If the parameter is a string (the first 10 characters are taken when the string is longer than 10 characters), the string will be used as a space.

If this parameter is not provided (or null), there will be no Spaces.

Value is commonly used. Replacers can be customized as required.

The return value

A JSON string representing the given value

abnormal

  • When inA circular referenceWill throw an exception TypeError (” Cyclic Object Value “)
  • When trying to switchBigIntType values will raise TypeError (“BigInt value can’t be serialized in JSON”).

features

Features a

1. Undefined, arbitrary functions, and symbol values are ignored during serialization (when they appear in property values of non-array objects) or converted to NULL (when they appear in arrays).

2. Properties of non-array objects are not guaranteed to appear in a serialized string in a particular order

const obj = {
    a: 1.b: undefined.// ignore
    c: '11'.d() {
        console.log('this is func');
    },   // ignore
    e: Symbol('tadm')   // ignore
}

console.log(JSON.stringify(obj));
// {"a":1,"c":"11"}

console.log(JSON.stringify([1.'11'.undefined.() = > 1 + 1.Symbol('tadm')));// [1,"11",null,null,null]
Copy the code

3. Function/undefined returns undefined when converted separately

console.log(JSON.stringify(undefined));
// undefined
console.log(JSON.stringify(() = > 1 + 1));
// undefined
Copy the code

4.NaN and Infinity values and null are treated as null

console.log(JSON.stringify(NaN));
// null
console.log(JSON.stringify(Infinity));
// null
console.log(JSON.stringify(null));
// null
Copy the code

Features two

If there is a toJSON() method, which defines what values will be serialized and ignores other attribute values

console.log(JSON.stringify({
    name: 'toJSON'.toJSON: () = > console.log('this is toJSON')}));// this is toJSON

console.log(JSON.stringify({
    name: 'toJSON'.toJSON: () = > console.log('this is toJSON'),
    toJSON: () = > 1 + 1,}));/ / 2
Copy the code

Features three

Booleans, numbers, and string wrapper objects are automatically converted to their original values during serialization

console.log(JSON.stringify(new Boolean(true)));
// true
console.log(JSON.stringify(new Number(1)));
/ / 1
console.log(JSON.stringify(new String('tadm')));
// "tadm"
Copy the code

Features four

Executing this method on objects that contain circular references (objects that refer to each other in an infinite loop) throws an error

const obj = {
    a: 1,
}
obj.b = obj;

console.log(JSON.stringify(obj));
// TypeError: Converting circular structure to JSON
// --> starting at object with constructor 'Object'
// --- property 'f' closes the circle
Copy the code

Features five

Date the Date is converted to a string by calling toJSON() (same as date.toisostring ()), so it is treated as a string

const date = new Date(a);console.log(JSON.stringify(date), date.toISOString());
/ / "the 2021-10-14 T07: far. 112 z" 2021-10-14 T07: far. 112 z
Copy the code

Features 6

All properties with symbol as the property key are completely ignored, even if they are mandatory in the replacer parameter

Copy the code

Features seven

Other types of objects, including Map/Set/WeakMap/WeakSet, serialize only enumerable properties

const a = new Map(a); a.set('x'.1);
console.log(a, '-- -- -- -- -- -.JSON.stringify(a));
// Map(1) { 'x' => 1 } ------ {}
// All other effects are the same

const obj = Object.create(null, {
    x: { value: 1 },
    y: { value: 2.enumerable: true}});console.log(Object.getOwnPropertyDescriptors(obj));
/** * { x: { value: 1, writable: false, enumerable: false, configurable: false }, y: { value: 2, writable: false, enumerable: true, configurable: false } } **/
console.log(JSON.stringify(obj));
// {"y":2}
Copy the code

Features eight

An error is also thrown when converting BigInt

JSON.stringify(BigInt('666'))
// Uncaught TypeError: Do not know how to serialize a BigInt
Copy the code

Use the advanced

replacer

function

const foo = {
    foundation: "Mozilla".model: "box".week: 45.transport: "car".month: 7
};

const replacer = (key, value) = > {
    if (typeof value === "string") {
        return undefined;
    }
    return value;
}

console.log(JSON.stringify(foo, replacer));
// {"week":45,"month":7}
Copy the code

An array of

console.log(JSON.stringify(foo, ['foundation'.'transport']));
// {"foundation":"Mozilla","transport":"car"}
Copy the code

space

console.log(JSON.stringify(foo, null.2));  // Display two Spaces
/** * { "foundation": "Mozilla", "model": "box", "week": 45, "transport": "car", "month": 7 } **/
Copy the code

Source analysis

If you don’t call toString(), you’ll call toString()

implementation

const stringify = (data) = > {
    const isCyclic = (obj) = > {
        let set = new Set(a);let flag = false;

        const deep = (obj) = > {
            if (obj && typeofobj ! ='object') {
                return;
            }
            if (set.has(obj)) {
                return flag = true;
            }
            set.add(obj);

            // Fix object.create (null)
            if(! obj? .hasOwnProperty) { obj.hasOwnProperty =new Object().hasOwnProperty;
            }

            for (let key in obj) {
                if(obj.hasOwnProperty(key)) { deep(obj[key]); }}// Get rid of it
            set.delete(obj);
        }

        deep(obj);

        return flag;
    }

    if (isCyclic(data)) {
        throw new TypeError('Converting circular structure to JSON');
    }

    if (typeof data === 'bigint') {
        throw new TypeError('Do not know how to serialize a BigInt');
    }

    const type = typeof data;
    const commonKeys1 = ['undefined'.'function'.'symbol'];
    const commonKeys2 = [NaN.Infinity.null];
    const getType = (s) = > {
        return Object.prototype.toString.call(s).replace(/\[object (.*?)\]/.'$1').toLowerCase()
    };

    if(type ! = ='object' || data === null) {
        let result = data;
        if (commonKeys2.includes(data)) {
            result = null;
        } else if (commonKeys1.includes(type)) {
            return undefined;
        } else if (type === 'string') {
            result = `"${data}"`;
        }

        return String(result);
    } else if (type === 'object') {
        // The object itself has toJSON methods
        if (typeof data.toJSON === 'function') {
            // Return the result and execute the above process
            return stringify(data.toJSON());
        } else if (Array.isArray(data)) {
            let result = data.map((it) = > {
                return commonKeys1.includes(typeof it) ? 'null' : stringify(it);
            })

            return ` [${result}] `.replace(/'/g.'"');
        } else {
            // Wrap objects for booleans, numbers, and strings
            if (['boolean'.'number'].includes(getType(data))) {
                return String(data);
            } else if (getType(data) === 'string') {
                return `"${data}"`;
            } else {
                let result = [];
                // iterate over all keys
                Object.keys(data).forEach((key) = > {
                    if (typeofkey ! = ='symbol') {
                        const value = data[key];

                        if(! commonKeys1.includes(typeof value)) {
                            // Note that if value is still an object, recursive processing is required
                            result.push(`"${key}":${stringify(value)}`)}}})return ` {${result}} `.replace(/ / '.'"'); }}}}Copy the code

test

console.log(stringify({
    toJSON: () = > 1 + 1
}))
/ / 2

const obj = {
    a: 1.b: undefined.c: '11'.d() {
        console.log('this is func');
    },
    e: Symbol('tadm'),
    // g: BigInt('666')
}
// obj.f = obj;

const obj = Object.create(null, {
    x: { value: 1 },
    y: { value: 2.enumerable: true}});console.log(stringify(obj));
// {"a":1,"c":"11"}
// {"y":2}
Copy the code

End

Json. stringify is just one of many apis that we use to manipulate objects, such as localStorage, because it can only store strings, we can just convert objects to strings. However, as mentioned above, it is easy to ignore the conversion results of non-normal data (undefined, null, BigInt, etc.), so be careful when using these features.