Small knowledge, big challenge! This paper is participating in theEssentials for programmers”Creative activities

The json.stringify method provides a global view of the depth of understanding of VARIOUS JS data types, its ability to handle extreme boundary cases, and its ability to encode JS. The reason why this article as this module is advanced, because I want to string together the knowledge points of the whole data type, so that the understanding of more comprehensive, to the next level.

This question is often asked during the front-end interview process. Most candidates only know what this method does, and if they were asked to implement a Json.srtingify method on their own, most would not be able to write it, or if they could write some of it, they would not have considered the problem thoroughly.

Therefore want to tamp their own JavaScript programming foundation, through practice to achieve some JS API ‘method, is very necessary, so to understand it.

Methods Basic introduction

Json.stringify is one of the most common methods used in everyday development on JSON objects, which contain two methods: parse() for parsing into JSON objects; The second is stringify(), a method for converting an object to a JSON string. Let’s look at the basic usage of the next two methods.

JSON.parse

The json.parse method is used to parse JSON strings and construct JavaScript values or objects described by the string. The method takes two arguments: the first argument is a JSON string to parse, and the second argument is optional: provides optional reviver functions to perform transformations on the resulting object before returning.

Parse (text[, reviver])

Here’s a code look at this method and the use of the Reviver parameter, as shown below.

const json = '{"result":true, "count":2}';

const obj = JSON.parse(json);

console.log(obj.count);

/ / 2

console.log(obj.result);

// true

/* With the second argument */

JSON.parse('{"p": 5}'.function (k, v) {

    if(k === ' ') return v;     // if k is not empty,

    return v * 2;              // Returns the value of the property multiplied by 2

});                            // { p: 10 }

Copy the code

The above code shows that you can return a jSON-formatted string as an object. With the second argument, you can do something to the string to be processed, such as multiplying the value of the attribute by 2 in the example above.

JSON.stringify

The json. stringify method converts a JavaScript object or value to a JSON string. By default, this method takes three arguments: the first is mandatory, and the last two are optional. The first argument is passed to the object to be converted; The second is a replacer function. If the specified replacer is an array, it optionally handles only the properties specified by the array. The third parameter controls the spacing within the resulting string. The latter two parameters are used less often overall.

Json.stringify (value[, replacer [, space]])

Let’s take a look at the next few parameters in a bit of code, as shown below.

JSON.stringify({ x: 1.y: 2 });

// "{"x":1,"y":2}"

JSON.stringify({ x: [10.undefined.function(){}, Symbol(' ')]})// "{"x":[10,null,null,null]}"

/* Example of the second argument */

function replacer(key, value) {

  if (typeof value === "string") {

    return undefined;

  }

  return value;

}

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

var jsonString = JSON.stringify(foo, replacer);

console.log(jsonString);

// "{"week":4,"month":7}"

/* Example of the third argument */

JSON.stringify({ a: 2 }, null."");

/* "{ "a": 2 }"*/

JSON.stringify({ a: 2 }, null."");

// "{"a":2}"

Copy the code

As you can see from the code above, adding the second parameter replacer makes a difference: filter out the attributes in the object as strings through the substitution method, and return numeric attributes as strings after Stringify; When the third argument is passed with multiple Spaces, it increases the amount of spacing in the resulting string, as you can see in the last code.

Let’s take a look at json.stringify’s internal conversions for various data types.

How do YOU do it manually?

To better understand the implementation process, think back to how much you know about the basics of JS datatypes. There are so many datatypes out there. If they all use this method, what will the result be?

Analyze various data types and boundary cases

To analyze what data types are passed in, passed in what will be returned, through the analysis of the results, in order to better achieve coding. A summary of the analysis is shown in the table below (refer to the MDN documentation).

JSON.stringify The input The output
Underlying data types undefined undefined
Underlying data types boolean “true”/”false”
Underlying data types number A numeric value of type string
Underlying data types symbol undefined
Underlying data types null “null”
Underlying data types string string
Underlying data types NaN and Infinity “null”
Reference data type Arrays include undefined, function, and symbol “null”
Reference data type RegExp “{}”
Reference data type Date The Data oftoJSON()A string value
Reference data type The common object 1. If you havetoJSONMethod, then serializetoJSON()The return value of the

2. If the attribute value is presentundefined, any function andsymbolValues, ignore

All 3.symbolProperties that are property keys are completely ignored

In the above table, the various data types are basically organized and returned by the · json.stringify · method, but there is a special case to note: executing this method on objects that contain circular references (also mentioned in the deep copy lecture) will throw an error.

Code logic implementation

First use typeof to separate the base data type from the reference data type, and then according to different cases to deal with different cases, according to this logic code implementation as follows.

function jsonStringify(data) {

  let type = typeof data;



  if(type ! = ='object') {

    let result = data;

    // Data may be an underlying data type

    if (Number.isNaN(data) || data === Infinity) {

       //NaN and Infinity serialize return "null"

       result = "null";

    } else if (type === 'function' || type === 'undefined' || type === 'symbol') {

      // Since function serialization returns undefined, it is processed with undefined and symbol

       return undefined;

    } else if (type === 'string') {

       result = '"' + data + '"';

    }

    return String(result);

  } else if (type === 'object') {

     if (data === null) {

        return "null"  // When typeof null is 'object

     } else if (data.toJSON && typeof data.toJSON === 'function') {

        return jsonStringify(data.toJSON());

     } else if (data instanceof Array) {

        let result = [];

        // If it is an array, then each item in the array may be multiple

        data.forEach((item, index) = > {

        if (typeof item === 'undefined' || typeof item === 'function' || typeof item === 'symbol') {

               result[index] = "null";

           } else{ result[index] = jsonStringify(item); }}); result ="[" + result + "]";

         return result.replace(/'/g.'"');

      } else {

         // Process common objects

         let result = [];

         Object.keys(data).forEach((item, index) = > {

            if (typeofitem ! = ='symbol') {

              //key if symbol object is ignored

              if(data[item] ! = =undefined && typeofdata[item] ! = ='function' && typeofdata[item] ! = ='symbol') {

                // if undefined, function, and symbol are attributes, ignore them

                result.push('"' + item + '"' + ":"+ jsonStringify(data[item])); }}});return ("{" + result + "}").replace(/'/g.'"'); }}}Copy the code

The basic code for manually implementing a json.stringify method is shown above, but there are a few things to note:

  1. Due to thefunctionreturn'undefined'And,typeof function Can directly return accurate judgment, so in the overall logic processing of the underlying data type, withundefined.symbol Dealt with directly;
  2. Because we talked about it beforetypeof null Is returned whenobject, therefore,nullThe whole judgment logic is in the logic that deals with reference data types;
  3. As for arrays in reference data types, there are many possibilities for the data type of each item in the array, so in the process of dealing with arrays, we willundefined.symbol.functionThe case that is one of the items in the array is treated specially
  4. And also at the end of dealing with ordinary objects,key(key) also has the same problem as arrays, so we need to apply the above cases (undefined.symbol.function) do special treatment;
  5. Finally, in the process of processing common objects, the problem of circular reference has not been detected, if there is a circular reference, need to throwError;

Overall, this code is quite complex, and there are a lot of things to consider if you write it on the spot during an interview. Of course, according to the above code is different from each person, you can also write your own better code, for example, you can also try to directly use the switch statement, respectively for special cases, the overall writing may look more clear than the above method, these can be determined according to their own situation.

Implementation effect test

The above method has been implemented, so will there be problems using it? Let’s do some use case testing with the code above.

Is the jsonStringify method implemented above the same as the real json.stringify method? See the test results below.

let nl = null;

console.log(jsonStringify(nl) === JSON.stringify(nl));

// true

let und = undefined;

console.log(jsonStringify(undefined) = = =JSON.stringify(undefined));

// true

let boo = false;

console.log(jsonStringify(boo) === JSON.stringify(boo));

// true

let nan = NaN;

console.log(jsonStringify(nan) === JSON.stringify(nan));

// true

let inf = Infinity;

console.log(jsonStringify(Infinity) = = =JSON.stringify(Infinity));

// true

let str = "jack";

console.log(jsonStringify(str) === JSON.stringify(str));

// true

let reg = new RegExp("\w");

console.log(jsonStringify(reg) === JSON.stringify(reg));

// true

let date = new Date(a);console.log(jsonStringify(date) === JSON.stringify(date));

// true

let sym = Symbol(1);

console.log(jsonStringify(sym) === JSON.stringify(sym));

// true

let array = [1.2.3];

console.log(jsonStringify(array) === JSON.stringify(array));

// true

let obj = {

    name: 'jack'.age: 18.attr: ['coding'.123].date: new Date(),

    uni: Symbol(2),

    sayHi: function() {

        console.log("hi")},info: {

        sister: 'lily'.age: 16.intro: {

            money: undefined.job: null}}}console.log(jsonStringify(obj) === JSON.stringify(obj));

// true

Copy the code

As you can see from the above test examples, the jsonStringify method implemented is basically the same as the result of json.stringify conversion, and it is not hard to see that jsonStringify basically meets the expected result.

conclusion

A json.stringify method is implemented by combining principle with practice. As you can see, implementing a Json.stringify method on its own as a whole is not easy, it relies on a lot of datatype knowledge and takes into account various boundary cases.

In addition, if the topic in this lecture as an interview question, in fact, is a very comprehensive investigation of JS coding ability, so it is necessary to systematically learn the relevant knowledge of data types, especially for the two methods of JSON, the few parameters that are not commonly used do you have an understanding? There is also the handling of arrays and ordinary objects in reference data types, which are more complicated to write by hand than the basic data types, and can be problematic in some details. So understand.