来谈谈JSON.stringify

JSON 在很多地方被用于不同服务/系统之间的数据交换。如今,所有主流编程语言都内置了对 JSON 数据解析和序列化的库支持。在这篇文章中,我们来谈谈 JavaScript 中的 JSON.stringify()。

首先来看一个对象转换处理的小例子。有一个需求是将下面这个对象:

javascript 复制代码
const todayILearn = {
  _id: 1,
  content: '今天学习 JSON.stringify(),我很开心!',
  created_at: 'Mon Nov 25 2019 14:03:55 GMT+0800 (中国标准时间)',
  updated_at: 'Mon Nov 25 2019 16:03:55 GMT+0800 (中国标准时间)'
}

转换为下面这个对象:

javascript 复制代码
const todayILearn = {
  id: 1,
  content: '今天学习 JSON.stringify(),我很开心!',
  createdAt: 'Mon Nov 25 2019 14:03:55 GMT+0800 (中国标准时间)',
  updatedAt: 'Mon Nov 25 2019 16:03:55 GMT+0800 (中国标准时间)'
}

这里的变化是:

  • 将属性名"_id"改为"id"。
  • 将属性名"created_at"和"updated_at"改为"createdAt"和"updatedAt"。
  • 值保持不变。

有很多方法可以实现上述需求。

  1. 遍历所有属性
javascript 复制代码
const todayILearnTemp = {};
for (const [key, value] of Object.entries(todayILearn)) {
  if (key === "_id") todayILearnTemp["id"] = value;
  else if (key === "created_at") todayILearnTemp["createdAt"] = value;
  else if (key === "updatedAt") todayILearnTemp["updatedAt"] = value;
  else todayILearnTemp[key] = value;
}
console.log(todayILearnTemp);
// 输出:
// { id: 1,
//  content: '今天学习 JSON.stringify(),我很开心!',
//  createdAt: 'Mon Nov 25 2019 14:03:55 GMT+0800 (中国标准时间)',
//  updated_at: 'Mon Nov 25 2019 16:03:55 GMT+0800 (中国标准时间)' 
// }

这种方法创建了一个新的临时变量,遍历所有属性并引入了很多 if - else 语句,一点也不优雅且不可扩展。 2. 删除并创建属性 可以添加三个新属性并删除三个不需要的属性。

javascript 复制代码
todayILearn.id = todayILearn._id;
todayILearn.createdAt = todayILearn.created_at;
todayILearn.updatedAt = todayILearn.updated_at;
delete todayILearn._id;
delete todayILearn.created_at;
delete todayILearn.updated_at;
console.log(todayILearn);
// 输出:
// { 
//  content: '今天学习 JSON.stringify(),我很开心!',
//  id: 1,
//  createdAt: 'Mon Nov 25 2019 14:03:55 GMT+0800 (中国标准时间)',
//  updatedAt: 'Mon Nov 25 2019 16:03:55 GMT+0800 (中国标准时间)' 
// }

它与上面的方法有相同的缺点,并且属性的顺序也改变了。 3. 使用 JSON.stringify() JSON.stringify() 可以用来将不需要的属性名替换为想要的属性名。

javascript 复制代码
const mapObj = {
  _id: "id",
  created_at: "createdAt",
  updated_at: "updatedAt"
};
JSON.parse(
  JSON.stringify(todayILearn).replace(
    /_id|created_at|updated_at/gi,
    matched => mapObj[matched])
    )
// { 
// id: 1,
//  content: '今天学习 JSON.stringify(),我很开心!',
//  createdAt: 'Mon Nov 25 2019 14:03:55 GMT+0800 (中国标准时间)',
//  updatedAt: 'Mon Nov 25 2019 16:03:55 GMT+0800 (中国标准时间)' 
// }

现在看起来优雅多了。

现在让我们来了解一下 JSON.stringify() 的主要规则,以便在需要时能够正确有效地使用它。

规则 1

如果undefined、函数和符号出现在对象属性值、数组中或作为独立值,它们的输出值会有所不同。 猜猜下面代码片段的输出会是什么。

javascript 复制代码
const data = {
  a: "aaa",
  b: undefined,
  c: Symbol("dd"),
  fn: function() {
    return true;
  }
};
JSON.stringify(data); // 输出:?

// "{"a":"aaa"}"

undefined、函数和符号作为对象属性值出现时,在序列化时会被忽略。

那如果出现在数组中呢?

javascript 复制代码
JSON.stringify(["aaa", undefined, function aa() {
    return true
  }, Symbol('dd')])  // 输出:?

// "["aaa",null,null,null]"

undefined、函数和符号出现在数组中时,它们会被转换为null

如果它们独立出现呢?

javascript 复制代码
JSON.stringify(function a (){console.log('a')})
// undefined
JSON.stringify( undefined)
// undefined
JSON.stringify(Symbol('dd'))
// undefined

undefined、函数和符号独立出现时,它们将是undefined

规则 2

如果不是数组对象,序列化后的属性可能不会保持原始顺序。

javascript 复制代码
const data = {
  a: "aaa",
  b: undefined,
  c: Symbol("dd"),
  fn: function() {
    return true;
  },
  d: "ddd"
};
JSON.stringify(data); // 输出:?
// "{"a":"aaa","d":"ddd"}"

JSON.stringify(["aaa", undefined, function aa() {
    return true
  }, Symbol('dd'),"eee"])  // 输出:?

// "["aaa",null,null,null,"eee"]"

正如规则 1 所提到的,一些属性如果是undefined、函数或符号之一,在序列化时会被忽略。因此顺序可能无法保持。

规则 3

如果一个对象实现了toJSON()函数,JSON.stringify() 的输出值将是toJSON()函数的返回值。

javascript 复制代码
JSON.stringify({
    say: "hello JSON.stringify",
    toJSON: function() {
      return "today i learn";
    }
  })
// "today i learn"

规则 4

NaNInfinitynull将被序列化为null

javascript 复制代码
JSON.stringify(NaN)
// "null"
JSON.stringify(null)
// "null"
JSON.stringify(Infinity)
// "null"

规则 5

布尔值、数字或字符串对象将被转换为它们相应的原始值。

javascript 复制代码
JSON.stringify([new Number(1), new String("false"), new Boolean(false)]);
// "[1,"false",false]"

规则 6

只有可枚举的属性才能被序列化,包括Map/Set/WeakMap/WeakSet等。

javascript 复制代码
JSON.stringify( 
    Object.create(
        null, 
        { 
            x: { value: 'json', enumerable: false }, 
            y: { value: 'stringify', enumerable: true } 
        }
    )
);
// "{"y","stringify"}"

规则 7

很多人可能知道,深度克隆一个对象最简单但最粗暴的方法是使用JSON.parse(JSON.stringify(obj))。但如果要转换的对象包含循环引用,它会有一些意想不到的行为。

javascript 复制代码
const obj = {
  name: "loopObj"
};
const loopObj = {
  obj
};
obj.loopObj = loopObj;
function deepClone(obj) {
  return JSON.parse(JSON.stringify(obj));
}
deepClone(obj)
/**
 VM44:9 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 (<anonymous>)
    at deepClone (<anonymous>:9:26)
    at <anonymous>:11:13
 */

如果对象中有循环引用,在序列化时会抛出异常。

规则 8

即使定义了替换器(replacer)来处理符号,任何以符号作为键的属性都将被忽略。

javascript 复制代码
JSON.stringify({ [Symbol.for("json")]: "stringify" }, function(k, v) {
    if (typeof k === "symbol") {
      return v;
    }
  })

// undefined

替换器可以是一个函数或一个数组。如果是一个函数,它可以用来覆盖上面的很多规则。

javascript 复制代码
const data = {
  a: "aaa",
  b: undefined,
  c: Symbol("dd"),
  fn: function() {
    return true;
  }
};
// 不使用替换器时
JSON.stringify(data); 

// "{"a":"aaa"}"

// 使用替换器时
JSON.stringify(data, (key, value) => {
  switch (true) {
    case typeof value === "undefined":
      return " undefined";
    case typeof value === "symbol":
      return value.toString();
    case typeof value === "function":
      return value.toString();
    default:
      break;
  }
  return value;
})
// "{"a":"aaa","b":" undefined","c":"Symbol(dd)","fn":"function() {\n    return true;\n  }"}"

如果替换器是一个数组,它包含了在序列化时应该保留的属性列表,其余的将被删除。

javascript 复制代码
const jsonObj = {
  name: "JSON.stringify",
  params: "obj,replacer,space"
};

JSON.stringify(jsonObj, ["params"]);
// "{"params":"obj,replacer,space"}"

希望你现在对 JSON.stringify() 有了更好的理解。

相关推荐
red润3 分钟前
使用 HTML5 Canvas 实现动态蜈蚣动画
前端·html·html5
sg_knight11 分钟前
VSCode如何修改默认扩展路径和用户文件夹目录到D盘
前端·ide·vscode·编辑器·web
一个处女座的程序猿O(∩_∩)O20 分钟前
完成第一个 Vue3.2 项目后,这是我的技术总结
前端·vue.js
mubeibeinv21 分钟前
项目搭建+图片(添加+图片)
java·服务器·前端
逆旅行天涯27 分钟前
【Threejs】从零开始(六)--GUI调试开发3D效果
前端·javascript·3d
小蜗牛慢慢爬行37 分钟前
有关异步场景的 10 大 Spring Boot 面试问题
java·开发语言·网络·spring boot·后端·spring·面试
m0_748255261 小时前
easyExcel导出大数据量EXCEL文件,前端实现进度条或者遮罩层
前端·excel
长风清留扬1 小时前
小程序毕业设计-音乐播放器+源码(可播放)下载即用
javascript·小程序·毕业设计·课程设计·毕设·音乐播放器
web147862107231 小时前
C# .Net Web 路由相关配置
前端·c#·.net
m0_748247801 小时前
Flutter Intl包使用指南:实现国际化和本地化
前端·javascript·flutter