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

相关推荐
Micheal_Wayne几秒前
“无关紧要”的小知识点:“xx Packages Are Looking for Funding”——npm fund命令及运行机制
前端·npm·node.js
且从容.2 分钟前
vue下载后端提供的文件/播放音频文件
前端·javascript·vue.js
老码沉思录2 分钟前
React Native 全栈开发实战班 - 打包发布之热更新
javascript·react native·react.js
hibiscusxin4 分钟前
deepin系统下载pnpm cnpm等报错
前端
老码沉思录12 分钟前
React Native 全栈开发实战班 - 原生功能集成之第三方登录
javascript·react native·react.js
幸运小圣14 分钟前
Vue3 -- 集成sass【项目集成5】
前端·css·sass
Black蜡笔小新22 分钟前
H.265流媒体播放器EasyPlayer.js视频流媒体播放器关于直播流播放完毕是否能监听到
开发语言·javascript·h.265
XY.散人28 分钟前
初识算法 · 位运算(end)
算法·面试
树上有只程序猿43 分钟前
Go Web服务中如何优雅平滑重启?
前端·golang·xcode
思考的Joey1 小时前
一种纯前端的H5灰度方案
前端·javascript