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 havetoJSON Method, then serializetoJSON() The return value of the2. If the attribute value is present undefined , any function andsymbol Values, ignoreAll 3. symbol Properties 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:
- Due to the
function
return'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; - Because we talked about it before
typeof null
Is returned whenobject
, therefore,null
The whole judgment logic is in the logic that deals with reference data types; - 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 will
undefined
.symbol
.function
The case that is one of the items in the array is treated specially - 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; - 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 throw
Error
;
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.