preface

Json.stringify is a commonly used method in daily development. Can you really use it flexibly?

Before we move on to this article, Bao wants you to dive into Stringify with a few questions.

  • stringifyHow many arguments does a function take?
  • stringifyWhat are the serialization criteria?
    • What happens in function serialization?
    • Null, undefined, NaNAnd so on special values?
    • ES6After the increase ofSymbolThe type,BigIntWill there be special processing in the serialization process?
  • stringifyWhy not deep copy?
  • You can think of thatstringifyThe use of?

The context of the whole article is consistent with the following mind map, so you can make an impression.

Three parameters

In everyday programming, we often use the json.stringify method to convert an object to JSON string form.

const stu = {
    name: 'zcxiaobao'.age: 18
}

// {"name":"zcxiaobao","age":18}
console.log(JSON.stringify(stu));
Copy the code

But is Stringify really that simple? Let’s take a look at the definition of stringify in MDN.

MDN points out that: 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.

Does stringfy take more than one parameter? Of course, stringify takes three arguments.

Let’s take a look at the syntax and parameters of stringify:

JSON.stringify(value[, replacer [, space]])
Copy the code
  • value: The value to be sequenced as a JSON string.
  • replacer(optional)
    1. If the parameter is a function, each attribute of the serialized value is converted and processed by the function during serialization;
    2. If the argument is aAn array ofOnly the property names contained in the array will be serialized to the finalJSONIn a string
    3. If the parameter isnullOr not, all attributes of the object are serialized.
  • space(Optional): Specifies a blank string for indentation to beautify the output
    1. If the argument is a number, it represents how many Spaces there are. The upper limit is 10.
    2. If the value is less than 1, there are no Spaces
    3. 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
    4. If this parameter is not provided (or null), there will be no Spaces

replacer

Let’s try using replacer.

  1. replacerAs a function

Replacer is a function that takes two arguments, a key and a value, and both arguments are serialized.

At the beginning, the replacer function is passed an empty string as a key value representing the object to be stringify. It is important to understand that the replacer function does not immediately parse the object into key-value pairs, but rather passes in the object to be serialized. The attributes on each object or array are then passed in sequence. If the function returns undefined or function, the attribute value will be filtered out, and the rest will follow the return rule.

// repalcer takes two arguments key value
// Key value is each key value pair of the object
// Thus we can do simple filtering based on the type of key or value
function replacer(key, value) {
  if (typeof value === "string") {
    return undefined;
  }
  return value;
}
// function can be tested by itself
function replacerFunc(key, value) {
  if (typeof value === "string") {
    return () = > {};
  }
  return value;
}
const foo = {foundation: "Mozilla".model: "box".week: 45.transport: "car".month: 7};
const jsonString = JSON.stringify(foo, replacer);
Copy the code

JSON serialization {“week”:45,”month”:7}

If the replacer function returns undefined or function, the current value will not be ignored and will be replaced by null.

const list = [1.'22'.3]
const jsonString = JSON.stringify(list, replacer)
Copy the code

JSON serialization results in ‘[1, NULL,3]’

  1. replacerAs an array

Better understood as an array, filters the keys that appear in the array.

const foo = {foundation: "Mozilla".model: "box".week: 45.transport: "car".month: 7};
const jsonString = JSON.stringify(foo, ['week'.'month']);
Copy the code

The result of JSON serialization is {“week”:45,”month”:7}. Only the values of week and month are reserved.

Nine characteristics

Feature 1: undefined, function, Symbol value

  1. Appear in non-array object property values:undefined, arbitrary function,SymbolValues will be captured during serializationignore
  2. Appear in an array:undefined, arbitrary function,SymbolThe value will be converted to zeronull
  3. When converted separately: undefined is returned
// 1. These three types of object attribute values are ignored
const obj = {
  name: 'zc'.age: 18.// The function is ignored
  sayHello() {
    console.log('hello world')},// undefined is ignored
  wife: undefined.// Symbol values are ignored
  id: Symbol(111),
  // [Symbol('zc')]: 'zc',
}
{"name":"zc","age":18}
console.log(JSON.stringify(obj));

// 2. The three values in the array are converted to NULL
const list = [
  'zc'.18.// The function is converted to null
  function sayHello() {
    console.log('hello world')},// undefined converts to null
  undefined.// Symbol converts to null
  Symbol(111)]// ["zc",18,null,null,null]
console.log(JSON.stringify(list))

// 3. These three values converted separately will return undefined

console.log(JSON.stringify(undefined))  // undefined
console.log(JSON.stringify(Symbol(111))) // undefined
console.log(JSON.stringify(function sayHello() { 
  console.log('hello world')}))// undefined
Copy the code

Property 2: toJSON() method

If there is a toJSON() method, the serialized result returns the same value as the toJSON() method, and the rest of the values are ignored.

const obj = {
  name: 'zc'.toJSON(){
    return 'return toJSON'}}// return toJSON
console.log(JSON.stringify(obj));
Copy the code

Property three: Boolean, numeric, string wrapper object

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

JSON.stringify([new Number(1), new String("zcxiaobao"), new Boolean(true)]);
// [1,"zcxiaobao",true]
Copy the code

NaN Infinity NULL

Feature 4 focuses on special values in JavaScript, such as NaN and Infinity and NULL in Number. All three values are treated as NULL during serialization.

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

// Wrapping objects with Boolean values, numbers, and strings are automatically converted to their original values during serialization
// Implicit conversions call the wrapper class, so Number => NaN is called first
// Then convert to null
// 0/0 => Infinity => null
JSON.stringify([Number('123a'), +'123a'.0/0])
Copy the code

Feature 5: Date objects

The toJSON method (same as date.toisostring ()) is deployed on the Date object to convert it to a string, so json.stringify () will serialize the Date value into a time-format string.

/ / ": the 2022-03-06 T08 24:56. 138 z"
JSON.stringify(new Date())
Copy the code

Property 6: Symbol

When Symbol is used as a value, objects, arrays, and arrays alone are ignored, converted to NULL, and converted to undefined, respectively.

Similarly, all properties with Symbol as the property key are completely ignored, even if they are mandatory in the replacer parameter.

const obj = {
  name: 'zcxiaobao'.age: 18[Symbol('lyl')]: 'unique'
}
function replacer(key, value) {
  if (typeof key === 'symbol') {
    returnvalue; }}// undefined
JSON.stringify(obj, replacer);
Copy the code

From the above example, we can see that although we specify the return Symbol value by replacer, it will be ignored.

Feature 7: BigInt

Json. stringify specifies that attempts to convert a value of type BigInt will raise TypeError

const bigNumber = BigInt(1)
// Uncaught TypeError: Do not know how to serialize a BigInt
console.log(JSON.stringify(bigNumber))
Copy the code

Feature 8: Circular reference

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

Parse (json.stringify (obj))) is the simplest and most violent way to use deep copy in everyday development. However, deep copy under this method has a huge hole, and the key problem is that Stringify can’t handle circular references.

const obj = {
  name: 'zcxiaobao'.age: 18,}const loopObj = {
  obj
}
// Form a circular reference
obj.loopObj = loopObj;
JSON.stringify(obj)

/* Uncaught TypeError: Converting circular structure to JSON --> starting at object with constructor 'Object' | property 'loopObj' -> object with constructor 'Object' --- property 'obj' closes the circle at JSON.stringify (
      
       ) at 
       
        :10:6 */
       
      
Copy the code

Attribute 9: Enumerable properties

For serialization of objects (including Map/Set/WeakMap/WeakSet), Stringify also explicitly states that only enumerable properties will be serialized, in addition to some of the cases described above

// Non-enumerable attributes are ignored by default
// {"age":18}
JSON.stringify(
    Object.create(
        null,
        {
            name: { value: 'zcxiaobao'.enumerable: false },
            age: { value: 18.enumerable: true}}));Copy the code

Six use

localStorage

The localStorage object is used to store data for an entire site for a long time, without expiration, until it is manually deleted. Usually we store it as objects.

  1. Just calllocalStorageObject methods
const obj = {
  name: 'zcxiaobao'.age: 18
}
// Simply call localstorage.setitem ()

localStorage.setItem('zc', obj);

// The final result is [object object]
// Call localStorage failed
console.log(localStorage.getItem('zc'))
Copy the code
  1. localStorageCooperate withJSON.stringifymethods

localStorage.setItem('zc'.JSON.stringify(obj));

{name: 'zcxiaobao', age: 18}
console.log(JSON.parse(localStorage.getItem('zc')))
Copy the code

Attribute filter

Imagine a scenario where the back end returns a long object with many properties, and we only need a few of them, and we want to store them in localStorage.

  1. Plan 1: Deconstruct assignment +stringify
// We only need a,e,f attributes
const obj = {
  a:1.b:2.c:3.d:4.e:5.f:6.g:7
}
// Destruct the assignment
const {a,e,f} = obj;
// Store to localStorage
localStorage.setItem('zc'.JSON.stringify({a,e,f}))
// {"a":1,"e":5,"f":6}
console.log(localStorage.getItem('zc'))
Copy the code
  1. usestringifyreplacerparameter
// Use replacer as an array for filtering
localStorage.setItem('zc'.JSON.stringify(obj, ['a'.'e'.'f']))
// {"a":1,"e":5,"f":6}
console.log(localStorage.getItem('zc'))
Copy the code

When a replacer is an array, it’s a nice trick to simply filter out the attributes we want.

Look before you leap

Parse (json.stringify) is one of the simplest and most violent ways to implement a deep copy of an object. But as the title suggests, use deep copy this way with great deliberation.

  1. Circular reference problem,stringifycomplains
  2. The function,undefined,SymbolWill be ignored
  3. NaN,Infinity-InfinityWill be serialized intonull
  4. .

Use json.parse (json.stringify) to make deep copies. Without these pitfalls, json.parse (json.stringify) is a viable deep-copy solution.

Object map function

When programming with arrays, we often use the map function. Once we have the replacer parameter, we can use this parameter to implement the object’s map function.

const ObjectMap = (obj, fn) = > {
  if (typeoffn ! = ="function") {
    throw new TypeError(`${fn}is not a function ! `);
  }
  // call json.stringify (obj, replacer) to implement map
  // Then call json.parse to convert it back to an object
  return JSON.parse(JSON.stringify(obj, fn));
};

// For example, multiply the value of the obj attribute by 2

const obj = {
  a: 1.b: 2.c: 3
}
console.log(ObjectMap(obj, (key, val) = > {
  if (typeof value === "number") {
    return value * 2;
  }
  return value;
}))
Copy the code

Return value * 2; return value * 2;

As mentioned earlier, the replacer function first passes in the object to be serialized, and the object * 2 => NaN => toJSON(NaN) => undefined => is ignored and there is no further key-value pair parsing.

Deleting object Properties

With the replacer function, we can also delete certain attributes of an object.

const obj = {
  name: 'zcxiaobao'.age: 18
}
// {"age":18}
JSON.stringify(obj, (key, val) = > {
  // This property is ignored when the return value is undefined
  if (key === 'name') {
    return undefined;
  }
  return val;
})
Copy the code

Object to determine

Json.stringify can serialize objects as strings, so we can use string methods to implement simple object equality judgments.

// Check whether the array contains an object
const names = [
  {name:'zcxiaobao'},
  {name:'txtx'},
  {name:'mymy'},];const zcxiaobao = {name:'zcxiaobao'};
// true
JSON.stringify(names).includes(JSON.stringify(zcxiaobao))
 
// Determine whether objects are equal
const d1 = {type: 'div'}
const d2 = {type: 'div'}

// true
JSON.stringify(d1) === JSON.stringify(d2);
Copy the code

Array objects are deduplicated

Using the above ideas, we can also implement simple array object decrement.

But since json.stringify serializes {x:1, y:1} and {y:1, x:1} differently, we need to deal with the objects in the array before we start.

  1. Method 1: Arrange the keys of each object in the array in lexicographical order
arr.forEach(item= > {
  const newItem = {};
  Object.keys(item)   // Get the object key
        .sort()       // Sort by key
        .map(key= > { // Generate a new object
          newItem[key] = item[key];
        })
  // Use newItem to de-duplicate operations
})
Copy the code

Json.stringify provides a replacer array format parameter to filter the array.

  1. Method two: with the help ofreplacerThe array format
function unique(arr) {
  const keySet = new Set(a);const uniqueObj = {}
  // Extract all keys
  arr.forEach(item= > {
    Object.keys(item).forEach(key= > keySet.add(key))
  })
  const replacer = [...keySet];
  arr.forEach(item= > {
    // All objects are replacer filtered according to the specified key value
    unique[JSON.stringify(item, replacer)] = item;
  })
  return Object.keys(unique).map(u= > JSON.parse(u))
}

// Test it
unique([{}, {}, 
      {x:1},
      {x:1},
      {a:1},
      {x:1.a:1},
      {x:1.a:1},
      {x:1.a:1.b:1}])// Return the result[{}, {"x":1}, {"a":1}, {"x":1."a":1}, {"x":1."a":1."b":1}]
Copy the code

Refer to the link

  • You don’t know the power of json.stringify ()
  • Parse (json.stringify (obj)) the disadvantages of implementing deep copy

After the language

I am battlefield small bag, a fast growing small front end, I hope to progress together with you.

If you like xiaobao, you can pay attention to me in nuggets, and you can also pay attention to my small public number – Xiaobao learning front end.

All the way to the future!!

An early end to the epidemic will restore peace to the world