JSON.stringify 的 “魅力”

前奏

JSON.stringify() 的使用大家肯定不陌生,一般在前端给后端传数据的时候用的多,但它隐藏的“秘密” 你知道多少呢,让我们一起来探索一下吧~。

JSON.stringify()

用于将 JavaScript 值转换为 JSON 字符串

语法

JSON.stringify(value[, replacer[, space]])
复制代码

参数

  • value: 必须,通常是 JavaScript 值(通常为对象或者数组)

  • replacer: 可选。

    • 如果该参数是一个函数,则每个被序列化的值的每个属性都会被这个函数处理一遍
    • 如果该参数是一个数组,则只有属性名在这个数组中的属性才会被序列化
  • space: 可选。

    • 指定缩进的空白字符串,美化输出
    • 如果该参数是一个数字,表示有多少个空格,上限为10,小于1则无空格
    • space 也可以使用非数字,如:\t
    • 如果是字符串,该字符串被作为空格。

返回值

一个表示给定值的 JSON 字符串。

基本使用

  • JSON.stringify() 可以转换对象或者值。(一般来说转换的是对象)

    // 1、转换对象
    JSON.stringify({
        name: "xiaoqi",
        age: 20
      })
    // '{"name":"xiaoqi","age":20}'// 2、转换值
    console.log(JSON.stringify(1)); //  "1"
    console.log(JSON.stringify("xiaoqi")); // "xiaoqi"
    console.log(JSON.stringify(true)); // "true"
    console.log(JSON.stringify(null)); // "null"
    复制代码
  • replacer 指定为函数,选择性的转换

      JSON.stringify(
        {
          name: "xiaoqi",
          age: 20
        },
        (key, value) => {
          console.log(key, value, "->>>>>");
          return value;
        }
      )
    复制代码

    image.png

    可以看到第一行输出的是原对象。当 replacer 是函数时,第一次参数不是对象的键值对,key 是空串,value是原对象。

    利用函数的这个特性可以对打破 JSON.stringify() 本身的一些特性。

    const obj = {
      name: "xiaoqi",
      age: undefined,
      special: Symbol("dcd"),
      hobby: () => "programming"
    };
    ​
    console.log(JSON.stringify(obj)); // '{"name":"xiaoqi"}'console.log(
      JSON.stringify(obj, (key, value) => {
        const type = typeof value;
        switch (true) {
          case type === "undefined":
            return "undefined";
          case type === "symbol":
            return value.toString();
          case type === "function":
            return value.toString();
          default:
            break;
        }
        return value;
      })
    ); 
    // '{"name":"xiaoqi","age":"undefined","special":"Symbol(dcd)","hobby":"function hobby() { return "programming"; }"} '
    复制代码

    当后端需要前端为空值,'' 、null、undefined 时都不要传给后端时,也可以将replacer 设置为函数。

    const obj = {
      name: "xiaoqi",
      age: undefined,
      height: null,
      text: ""
    };
    console.log(JSON.stringify(obj, (key, value) => (!value ? undefined : value)));
    // '{"name":"xiaoqi"}'
    复制代码
  • replacer 指定为数组时,相当于一个过滤器,可以转换指定的属性。

    console.log(
      JSON.stringify(
        {
          name: "xiaoqi",
          age: 20
        },
        ["name"]
      )
    );
    // '{"name":"xiaoqi"}'
    复制代码
  • 使用第三个参数 space 来美化输出

     JSON.stringify({
          name: "xiaoqi",
          age: 20
        }, null,'\t')
    /* 
    '{
        "name": "xiaoqi",
        "age": 20
     }' 
    */
    复制代码

特性

  • undefined、函数、symbol 值在非数组对象中会被忽略。

    const obj = {
      name: "xiaoqi",
      age: undefined,
      special: Symbol("dcd"),
      hobby: () => "programming"
    };
    console.log(JSON.stringify(obj)); // '{"name":"xiaoqi"}'
    复制代码
  • undefined、函数、symbol 值在数组中会被转换为 null

    const obj = ["xiaoqi", undefined, Symbol("dcd"), () => "programming"];
    console.log(JSON.stringify(obj)); // ["xiaoqi",null,null,null] 
    复制代码
  • undefined、函数、symbol 值在单独转换是会被转换为 undefined

    console.log(JSON.stringify(undefined)); // undefined
    console.log(JSON.stringify(Symbol("dcd"))); // undefined
    console.log(JSON.stringify(() => "programming")); // undefined
    复制代码
  • 布尔值、字符串、数字的包装对象会被转换为原始值。

    // 包装对象
    console.log(JSON.stringify([new Number(1), new Boolean(false), new String("112")]));
    // [1,false,"112"] 
    复制代码
  • 所有以 symbol 值作为 key 的属性都会被忽略,即使在 replacer 函数强制返回也不行。

    console.log(JSON.stringify({[Symbol("dcd")]: "xiaoxi" })); // '{}'
    console.log(
      JSON.stringify({ [Symbol("dcd")]: "xiaoxi" }, (key, value) => {
        if (typeof key === "symbol") {
          return value;
        }
      })
    ); // undefined
    复制代码
  • Date 对象会被转换为字符串

    console.log(JSON.stringify(new Date())); // "2021-12-07T02:40:07.609Z"
    复制代码
  • RegExp对象会被转换为{}

    console.log(JSON.stringify(new RegExp())); // '{}'
    复制代码
  • NaN、Infinity 、null 作为属性值都会被转换为 null

    console.log(JSON.stringify({ name: null, age: NaN, height: Infinity })); 
    // {"name":null,"age":null,"height":null}
    复制代码
  • 转换的值如果存在 toJSON 方法,toJSON 方法返回的值将成为最终转换的值

    console.log(
      JSON.stringify({
        name: "xiaoqi",
        toJSON() {
          return "lalalla";
        }
      })
    );
    复制代码
  • 对于对象来说,仅仅只序列化可枚举的的属性

    const object1 = {};
    ​
    Object.defineProperty(object1, "age", {
      value: 42,
      enumerable: false
    });
    ​
    console.log(JSON.stringify(object1)); // '{}'
    复制代码
  • 循环引用的对象

    const obj1 = [{](url)
      name: "xiaoqi"
    };
    ​
    obj1.child = obj1;
    ​
    console.log(JSON.stringify(obj1)); // TypeError: Converting circular structure to JSON
    复制代码
  • 转换 BigInt 类型的值会报错

    console.log(JSON.stringify(BigInt(99999999999999))); // TypeError: Do not know how to serialize a BigInt
    复制代码

异常处理

JSON.stringify 的使用容易出错,在工作中我们一般都会封装一个安全的 jsonStringify。

/**
 * 对象转化为 JSON 格式字符串
 */
export const jsonStringify = (value: any) => {
  try {
    return JSON.stringify(value)
  } catch (err) {
    // 上报日志
    sendLog('error', `[jsonStringify] error: ${err.message}`, 'js_error')
    return "''"
  }
}
复制代码

尾声

参考文章

猜你喜欢

转载自juejin.im/post/7039890882572582949