8

foreword

A bug was encountered in the project. In order to retain a JSON object, a component uses JSON.stringify to convert it into a string. Of course, this is to avoid the pollution of the data source caused by the object being a reference type.

However, after using the JSON.parse method later, I found that the data has changed.

Code simplification:

 let obj = {
  name: 'Gopal',
  age: Infinity
}
let originObj = JSON.stringify(obj)
console.log(originObj) // {"name":"Gopal","age":null}

As you can see, Infinity becomes null, which leads to the following bug. In fact, there are already many pits in the project to step on JSON.stringify. I will take this opportunity to sort it out and give you a reference.

Let's talk about the solution to this problem first:

Workaround 1:

Simple and rude, re-assign the age attribute

Workaround 2:

 function censor(key, value) {
  if (value === Infinity) {
    return "Infinity";
  }
  return value;
}
var b = JSON.stringify(a, censor);

var c = JSON.parse(
  b,
  function (key, value) {
    return value === "Infinity"  ? Infinity : value;
  }
);

This is a bit of a detour. As a reference, I actually used the first method directly. But here you can see that JSON.stringify actually has a second parameter, so what's the use of it? Next we unveil its mystery.

JSON.stringify basic syntax

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

concept

The MDN Chinese documentation explains it as follows:

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

I personally think that this interpretation is inappropriate, the inappropriateness is "object or array", because in fact, for ordinary values, we can also use JSON.stringify, but we rarely use it. However, this problem is not big, and what we introduce in this article is also for objects or arrays.

 JSON.stringify('foo');   // '"foo"'

The English version of MDN's explanation will be much more reasonable:

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

In simple terms, JSON.stringify() converts a value to the corresponding JSON format string.

JSON.stringify powerful second parameter replacer

This parameter is optional and can be a function or an array

When it is a function, during the serialization process, each property that is serialized will be converted and processed by the function, see the following code:

 let replacerFun = function (key, value) {
  console.log(key, value)
  if (key === 'name') {
    return undefined
  }
  return value
}

let myIntro = {
  name: 'Gopal',
  age: 25,
  like: 'FE'
}

console.log(JSON.stringify(myIntro, replacerFun))
// {"age":25,"like":"FE"}

This is actually a filtering function, which uses the feature that the property value of the object in JSON.stringify is undefined and will be ignored in serialization (we will mention it later).

It is worth noting that at the beginning, the replacer function will be passed an empty string as the key value, representing the object to be stringified.

The values output by console.log(key, value) above are as follows:

 { name: 'Gopal', age: 25, like: 'FE' }
name Gopal
age 25
like FE
{"age":25,"like":"FE"}

It can be seen that through the second parameter, we can operate and modify the value of the serialized target more flexibly.

When the second parameter is an array, only the property names contained in the array will be serialized:

 JSON.stringify(myIntro, ['name']) // {"name":"Gopal"}

The third parameter of the fancy

Specify a blank string for indentation, and more often specify a number to represent several spaces:

 let myIntro = {
  name: 'Gopal',
  age: 25,
  like: 'FE'
}

console.log(JSON.stringify(myIntro))
console.log(JSON.stringify(myIntro, null, 2))

// {"name":"Gopal","age":25,"like":"FE"}
// {
//   "name": "Gopal",
//   "age": 25,
//   "like": "FE"
// }

JSON.stringify usage scenarios

Check if object/array values are equal

 let a = [1,2,3],
    b = [1,2,3];
JSON.stringify(a) === JSON.stringify(b);// true

localStorage/sessionStorage storage objects

We know that localStorage/sessionStorage can only store strings. When we want to store objects, we need to use JSON.stringify to convert them into strings, and then JSON.parse them when we get them.

 // 存
function setLocalStorage(key,val) {
    window.localStorage.setItem(key, JSON.stringify(val));
};
// 取
function getLocalStorage(key) {
    let val = JSON.parse(window.localStorage.getItem(key));
    return val;
};

Implement object deep copy

 let myIntro = {
  name: 'Gopal',
  age: 25,
  like: 'FE'
}

function deepClone() {
  return JSON.parse(JSON.stringify(myIntro))
}

let copyMe = deepClone(myIntro)
copyMe.like = 'Fitness'
console.log(myIntro, copyMe)

// { name: 'Gopal', age: 25, like: 'FE' } { name: 'Gopal', age: 25, like: 'Fitness' }

Route (browser address) parameter

Because browser parameters can only be passed through strings, JSON.stringify is also required.

Notes on using JSON.stringify

After reading the above, do you think JSON.stringify is very powerful, and immediately want to try it in the project? Wait a moment, read the following precautions before making a decision, it may trigger some difficult-to-find problems in some scenarios.

There is a toJSON method in the conversion attribute value, use it with caution

If there is a toJSON method in the converted value, the value returned by this method will be the final serialization result.

 // toJSON
let toJsonMyIntro = {
  name: "Gopal",
  age: 25,
  like: "FE",
  toJSON: function () {
    return "前端杂货铺";
  },
};

console.log(JSON.stringify(toJsonMyIntro)); // "前端杂货铺"

There are undefined, arbitrary functions and symbol values in the converted value, use with caution

There are two cases:

One is an array object, undefined, arbitrary functions and symbol values will be converted to null.

 JSON.stringify([undefined, Object, Symbol("")]);
// '[null,null,null]'

One is a non-array object, which is ignored during serialization.

 JSON.stringify({ x: undefined, y: Object, z: Symbol("") });
// '{}'

For this case, we can use the second parameter of JSON.stringify to make it meet our expectations

 const testObj = { x: undefined, y: Object, z: Symbol("test") }

const resut = JSON.stringify(testObj, function (key, value) {
  if (value === undefined) {
    return 'undefined'
  } else if (typeof value === "symbol" || typeof value === "function") {
    return value.toString()
  }
  return value
})

console.log(resut)
// {"x":"undefined","y":"function Object() { [native code] }","z":"Symbol(test)"}

Objects containing circular references, use with caution

 let objA = {
  name: "Gopal",
}

let objB = {
  age: 25,
}

objA.age = objB
objB.name = objA
JSON.stringify(objA)

The following error will be reported:

 Uncaught TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'Object'
    |     property 'age' -> object with constructor 'Object'
    --- property 'name' closes the circle
    at JSON.stringify (<anonymous>)
    at <anonymous>:1:6

Attributes with symbol as attribute key, use with caution

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

 JSON.stringify({ [Symbol.for("foo")]: "foo" }, [Symbol.for("foo")])
// '{}'

JSON.stringify({ [Symbol.for("foo")]: "foo" }, function (k, v) {
  if (typeof k === "symbol") {
    return "a symbol";
  }
})
// undefined

Values are NaN and Infinity, use with caution

Array values, or non-array object property values with NaN and Infinity values, are converted to null.

 let me = {
  name: "Gopal",
  age: Infinity,
  money: NaN,
};
let originObj = JSON.stringify(me);
console.log(originObj); // {"name":"Gopal","age":null,"money":null}

JSON.stringify([NaN, Infinity])
// [null,null]

Use with caution when having non-enumerable property values

Non-enumerable properties are ignored by default:

 let person = Object.create(null, {
  name: { value: "Gopal", enumerable: false },
  age: { value: "25", enumerable: true },
})

console.log(JSON.stringify(person))
// {"age":"25"}

Summarize

JSON.stringify is indeed very convenient to solve many of our problems in practical applications, such as simple deep copying and so on.

However, when we use it, we also need to know its shortcomings. In the case that the target value may be some special value, the result after serialization may not meet our expectations. At this time, we need to use it with caution.


Gopal
366 声望77 粉丝