来谈谈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() 有了更好的理解。

相关推荐
崔庆才丨静觅6 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60617 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了7 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅7 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅7 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅8 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment8 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅8 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊8 小时前
jwt介绍
前端