关于javascript:你不知道的-JSONstringify

37次阅读

共计 3960 个字符,预计需要花费 10 分钟才能阅读完成。

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

有幻想,有干货,微信搜寻【大迁世界】关注这个在凌晨还在刷碗的刷碗智。
本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试残缺考点、材料以及我的系列文章。

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

JSON.stringify({foo: "bar"});
// => '{"foo":"bar"}'

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

然而 JS 的许多中央都有问题,这个函数也不例外。咱们可能会设想一个叫做 “stringify “ 的函数总是返回一个字符串 …… 但它并没有!

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

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

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

  • 列举 JSON.stringify 不返回字符串的状况
  • 咱们将如何防止这些陷阱

什么时候 JSON.stringify 不返回字符串?

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

对蕴含循环援用的对象(对象之间互相援用,造成有限循环)执行此办法,会抛出谬误

我认为 JSON.stringify 可能返回字符串以外的货色是挺诧异的。但在 6 种状况下,它能够返回undefined:

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

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

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

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

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

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

JSON.stringify({toJSON: () => Symbol("heya") });
// => undefined
  1. 你能够传递第二个参数,称为 “replacer”,它能够扭转序列化的逻辑。如果这个函数为顶层返回上述值之一,JSON.stringify 将返回undefined
JSON.stringify({ignored: true}, () => undefined);
// => undefined

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

须要留神的是,其中的许多货色实际上只影响到顶层的序列化。例如,JSON.stringify({foo: undefined}),返回字符串"{}",这并不令人诧异。

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

const result: string = JSON.stringify(undefined);

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

JSON.stringify 也可能遇到问题,导致它抛出一个谬误。在失常状况下,有四种状况会产生:

  1. 循环援用会导致抛出一个类型谬误。
const b = {a};
a.b = b;

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

留神,这些谬误音讯在不同浏览器可能提醒是不样的,例如,Firefox 的错误信息与 Chrome 的不同。

  1. BigInts 不能用 JSON.stringify 进行序列化,这些也会导致一个 TypeError。
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 函数的对象将被运行。如果这些函数抛出谬误,它将冒泡到调用者。
const obj = {
  foo: "ignored",
  toJSON() {throw new Error("Oh no!");
  },
};

JSON.stringify(obj);
// => Error: Oh no!
  1. 你能够传递第二个参数,称为 replacer。如果这个函数抛出一个谬误,它将冒泡。
JSON.stringify({}, () => {throw new Error("Uh oh!");
});
// => Error: Uh oh!

当初咱们曾经看到了 JSON.stringify 不返回字符串的状况,接下来,咱们来看看如何防止这些问题。

如何防止这些问题

没有对于如何解决这些缺点的通用办法,所以这里只介绍一些常见的状况。

解决循环援用

依据集体教训,JSON.stringify 在传递循环援用时最容易出错。如果这对你来说是一个常见的问题,我举荐 json-stringify-safe 包,它能很好地解决这种状况。

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 的函数,它总是返回一个用于调试的字符串。就像这样

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的官网定义在这里并不正确。它们实际上看起来像这样:

// Note: 这外面简化过
interface JSON {
  // ...
  stringify(value: any): string;
}

可怜的是,这是一个长期存在的问题,没有一个完满的解决方案。

你能够尝试修补 JSON.stringify 的类型,但每个解决方案都有肯定的毛病。我倡议用自定义类型定义本人的包装器并。例如,Signal Desktop 的 reallyJsonStringify 的模板:

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… 已收录,有一线大厂面试残缺考点、材料以及我的系列文章。

正文完
 0