This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.
JsonStringify is a very frequently used API, but it has one feature that we need to be aware of in order to avoid pitfalls in our code. Let’s implement a simple version of the jsonStringify function
Json.stringify has six features
Features a
Booleans, numbers, and string wrapper objects are automatically converted to their original values during serialization
Now there is an object like this:
const obj = {
bol: new Boolean(true),
num: new Number(1),
str: new String(1)}Copy the code
Use Typeof to detect the data types of obJ attributes
typeof obj.bol; // object
typeof obj.num; // object
typeof obj.str; // object
Copy the code
After serializing Stringify
JSON.stringify(obj); // {"bol":true,"num":1,"str":"1"}
Copy the code
It is then parsed and parsed for the data types of each property
const stringifyObj = JSON.parse(JSON.stringify(obj));
typeof stringifyObj.bol; // boolean
typeof stringifyObj.num; // number
typeof stringifyObj.str; // string
Copy the code
Features two
NaN, Infinity, -infinity, and NULL are all treated as null when serializing Stringify
const obj = {
nan: NaN.infinity: Infinity.null: null};JSON.stringify(obj); // {"nan":null,"infinity":null,"null":null}
Copy the code
Features three
When an object is serialized, if it has a toJSON function, the value returned by this function is the result of the entire object being serialized
const obj = {
nan: NaN.infinity: Infinity.null: null.toJSON() {
return "Have toJSON function"; }};JSON.stringify(obj); // "Have toJSON function"
Copy the code
You can see that the serialized data only contains the return value of the toJSON function, ignoring the rest of the data
⚠️ : Date data will be properly serialized because the toJSON function is deployed on Date, which can be seen by printing date.prototype.tojson to the console
const obj = {
date: new Date()};JSON.stringify(obj); / / {" date ":" the 2021-10-08 T11:43:31. 881 z}"
Copy the code
Features four
Undefined, function, and symbol behave differently
As an object key-value pair:
As the value:
const obj = {
undefined: undefined.fn() {},
symbol: Symbol()
};
JSON.stringify(obj); / / {}
Copy the code
As the key:
const fn = function () {};
const obj = {
[undefined] :undefined,
[fn]: function () {},Symbol()] :Symbol()
};
JSON.stringify(obj); / / {}
Copy the code
Undefined, function, and symbol as the key and value of an object are ignored during serialization
⚠️ : The original order of objects may be changed at this point, because the above three types of data are ignored during serialization
As an array value:
const arr = [undefined.function fn() {}, Symbol()];
JSON.stringify(arr); // [null,null,null]
Copy the code
Undefined, function, and symbol as values of arrays are all converted to NULL on serialization
Standing alone:
JSON.stringify(undefined); // undefined
JSON.stringify(function () {}); // undefined
JSON.stringify(Symbol()); // undefined
Copy the code
Undefined, function, and symbol are all converted to undefined on serialization when they stand alone
Features five
During serialization, only enumerable attributes are serialized; non-enumerable attributes are ignored
const obj = {
name: "nordon".age: 18};// Change age to non-enumerable
Object.defineProperty(obj, "age", {
enumerable: false});JSON.stringify(obj); // {"name":"nordon"}
Copy the code
⚠️ : This also changes the original order of objects
Features 6
Objects that are referred to in a loop will throw an exception on serialization
const obj = {
name: "nordon".age: 18};const p = {
name: 'wy',
obj
}
obj.p = p
JSON.stringify(obj);
Copy the code
This causes the console to throw an exception:
Uncaught TypeError: Converting circular structure to JSON --> starting at object with constructor 'Object' | property 'p' -> object with constructor 'Object' --- property 'obj' closes the circle at JSON.stringify (<anonymous>)
Implement Stringify manually
Now that you know some of the features of json.stringify, you can start implementing a version of Kack based on these features
Before implementing it, we’ll use The Currization to encapsulate some utility functions for datatype validation:
const currying = (fn, ... outParams) = > {
// Get the number of arguments required by the fn function
const paramsLen = fn.length;
return (. args) = > {
// Collect all parameters
let params = [...outParams, ...args];
// If the parameter does not reach the parameter required by fn, continue to collect parameters
if (params.length < paramsLen) {
returncurrying(fn, ... params); }returnfn(... params); }; };/** * type: type - [object Array], [object Number] * source: data source */
const judgeType = (type, source) = > {
return Object.prototype.toString.call(source) === type;
};
const isUndefined = currying(judgeType, "[object Undefined]");
const isSymbol = currying(judgeType, "[object Symbol]");
const isFunction = currying(judgeType, "[object Function]");
const isObject = currying(judgeType, "[object Object]");
const isNull = currying(judgeType, "[object Null]");
Copy the code
Let’s go straight to the code:
function jsonStringify(data) {
let type = typeof data;
if (isNull(data)) {
// null directly returns the string 'null'
return "null";
} else if (data.toJSON && typeof data.toJSON === "function") {
// The toJSON function is configured to directly use the data returned by toJSON and ignore other data
return jsonStringify(data.toJSON());
} else if (Array.isArray(data)) {
let result = [];
// If it is an array, then each item in the array may be multiple
data.forEach((item, index) = > {
if (isUndefined(item) || isSymbol(item) || isFunction(item)) {
result[index] = "null";
} else{ result[index] = jsonStringify(item); }}); result ="[" + result + "]";
return result.replace(/'/g.'"');
} else if (isObject(data)) {
// 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.'"');
} else 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 (isUndefined(data) || isSymbol(data) || isFunction(data)) {
// Since function serialization returns undefined, it is processed with undefined and symbol
return undefined;
} else if (type === "string") {
result = '"' + data + '"';
}
return String(result); }}Copy the code
This simplified version of json.stringify is complete, although it lacks a lot of power, mainly to provide an idea. The core comments are already commented in the code and can be understood in conjunction with the code and the above features