前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >你不知道的 JSON.stringify

你不知道的 JSON.stringify

作者头像
前端小智@大迁世界
发布2022-06-15 15:15:03
3.3K0
发布2022-06-15 15:15:03
举报
文章被收录于专栏:终身学习者

作者:BlackLivesMatter 译者:前端小智 来源:devinduct

本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试完整考点、资料以及我的系列文章。

JSON.stringify是我们经常用到的的一个方法,它主要作用是将 JavaScript 值和对象转换为字符串。如:

代码语言:javascript
复制
JSON.stringify({ foo: "bar" });
// => '{"foo":"bar"}'

JSON.stringify(123);
// => '123'

但是JS 的许多地方都有问题,这个函数也不例外。我们可能会想象一个叫做 "stringify "的函数总是返回一个字符串......但它并没有!

例如,如果你尝试 stringify undefined,它返回 undefined ,而不是一个字符串。

代码语言:javascript
复制
JSON.stringify(undefined);
// => undefined

接下来,我将分两部分讲:

  • 列举 JSON.stringify 不返回字符串的情况
  • 我们将如何避免这些陷阱
什么时候 JSON.stringify 不返回字符串?

undefined、任意的函数以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)。函数、undefined 被单独转换时,会返回 undefined

对包含循环引用的对象(对象之间相互引用,形成无限循环)执行此方法,会抛出错误

我认为 JSON.stringify 能够返回字符串以外的东西是挺惊讶的。但在6种情况下,它可以返回undefined:

  1. 试图在顶层对 undefined 进行序列化,会返回 undefined
代码语言:javascript
复制
JSON.stringify(undefined);
// => undefined
  1. 尝试序列化函数也会返回 undefined。对于常规函数、箭头函数、异步函数和生成器函数都是如此。
代码语言:javascript
复制
JSON.stringify(function foo() {});
// => undefined

JSON.stringify(() => {});
// => undefined

function bar() {}
bar.someProperty = 123;
JSON.stringify(bar);
// => undefined
  1. 尝试序列化symbol 也会返回 undefined
代码语言:javascript
复制
JSON.stringify(Symbol("computers were a mistake"));
// => undefined
  1. 在浏览器中,试图序列化被废弃的 document.all 也会返回 undefined
代码语言:javascript
复制
// => undefined

这只影响到浏览器,因为document.all在其他环境中是不可用的,比如Node。

  1. 带有 toJSON 函数的对象将被运行,而不是试图正常地序列化它们。但是如果 toJSON 返回上面的一个值,试图在顶层序列化它将导致 JSON.stringify 返回undefined
代码语言:javascript
复制
JSON.stringify({ toJSON: () => undefined });
// => undefined

JSON.stringify({ ignored: true, toJSON: () => undefined });
// => undefined

JSON.stringify({ toJSON: () => Symbol("heya") });
// => undefined
  1. 你可以传递第二个参数,称为 "replacer",它可以改变序列化的逻辑。如果这个函数为顶层返回上述值之一,JSON.stringify 将返回undefined
代码语言:javascript
复制
JSON.stringify({ ignored: true }, () => undefined);
// => undefined

JSON.stringify(["ignored"], () => Symbol("hello"));
// => undefined

需要注意的是,其中的许多东西实际上只影响到顶层的序列化。例如,JSON.stringify({foo: undefined}),返回字符串"{}",这并不令人惊讶。

我还想提一下,TypeScript的类型定义在这里是不正确的。例如,下面的代码类型的校验可以通过:

代码语言:javascript
复制
const result: string = JSON.stringify(undefined);

在第2部分中,我们将讨论如何更新 TypeScript 的定义以确保其正确性。

JSON.stringify 也可能遇到问题,导致它抛出一个错误。在正常情况下,有四种情况会发生:

  1. 循环引用会导致抛出一个类型错误。
代码语言:javascript
复制
const b = { a };
a.b = b;

JSON.stringify(a);
// => TypeError: cyclic object value

注意,这些错误消息在不同浏览器可能提示是不样的,例如,Firefox 的错误信息与Chrome的不同。

  1. BigInts不能用 JSON.stringify 进行序列化,这些也会导致一个TypeError。
代码语言:javascript
复制
JSON.stringify(12345678987654321n);
// => TypeError: BigInt value can't be serialized in JSON

JSON.stringify({ foo: 456n });
// => TypeError: BigInt value can't be serialized in JSON
  1. 带有 toJSON 函数的对象将被运行。如果这些函数抛出错误,它将冒泡到调用者。
代码语言:javascript
复制
const obj = {
  foo: "ignored",
  toJSON() {
    throw new Error("Oh no!");
  },
};

JSON.stringify(obj);
// => Error: Oh no!
  1. 你可以传递第二个参数,称为 replacer。如果这个函数抛出一个错误,它将冒泡。
代码语言:javascript
复制
JSON.stringify({}, () => {
  throw new Error("Uh oh!");
});
// => Error: Uh oh!

现在我们已经看到了 JSON.stringify 不返回字符串的情况,接下来,我们来看看如何避免这些问题。

如何避免这些问题

没有关于如何解决这些缺陷的通用方法,所以这里只介绍一些常见的情况。

处理循环引用

根据个人经验,JSON.stringify 在传递循环引用时最容易出错。如果这对你来说是一个常见的问题,我推荐 json-stringify-safe 包,它能很好地处理这种情况。

代码语言:javascript
复制
const stringifySafe = require("json-stringify-safe");

const a = {};
const b = { a };
a.b = b;

JSON.stringify(a);
// => TypeError: cyclic object value

stringifySafe(a);
// => '{"b":{"a":"[Circular ~]"}}'
封装

你可能想用你自己的自定义函数来封装 JSON.stringify。你可以决定你想要它做什么。错误应该冒出来吗?如果 JSON.stringify 返回 undefined,应该怎么做?

例如,Signal Desktop有一个名为 reallyJsonStringify 的函数,它总是返回一个用于调试的字符串。就像这样

代码语言:javascript
复制
function reallyJsonStringify(value) {
  let result;
  try {
    result = JSON.stringify(value);
  } catch (_err) {
    // If there's any error, treat it like `undefined`.
    result = undefined;
  }

  if (typeof result === "string") {
    // It's a string, so we're good.
    return result;
  } else {
    // Convert it to a string.
    return Object.prototype.toString.call(value);
  }
}
关于TypeScript类型的说明

如果你已经在用 TypeScript,可能会惊讶地发现,TypeScript对 JSON.stringify的官方定义在这里并不正确。它们实际上看起来像这样:

代码语言:javascript
复制
// Note: 这里面简化过
interface JSON {
  // ...
  stringify(value: any): string;
}

不幸的是,这是一个长期存在的问题,没有一个完美的解决方案。

你可以尝试修补 JSON.stringify 的类型,但每个解决方案都有一定的缺点。我建议用自定义类型定义自己的包装器并。例如,Signal Desktop的reallyJsonStringify 的模板:

代码语言:javascript
复制
function reallyJsonStringify(value: unknown): string {
  // ...

总结

  • JSON.stringify 有时会返回 undefined,而不是一个字符串
  • JSON.stringify 有时会抛出一个错误
  • 我们可以通过用不同的方式包装函数来解决这个问题

希望这篇文章能让你对 JSON.stringify 有更全面的了解。

我是刷碗智,励志退休后要回家摆地摊的人,我们下期见。


代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

原文:https://evanhahn.com/when-str...

交流

本文 GitHub https://github.com/qq44924588... 已收录,有一线大厂面试完整考点、资料以及我的系列文章。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-06-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么时候 JSON.stringify 不返回字符串?
  • 如何避免这些问题
    • 处理循环引用
      • 封装
        • 关于TypeScript类型的说明
        • 总结
        • 交流
        相关产品与服务
        文件存储
        文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档