By BlackLivesMatter translator: DevInduct

Have a dream, have dry goods, wechat search [big Move the world] pay attention to this in the early morning is still in the bowl washing wisdom. In this paper, making github.com/qq449245884… Has been included, a line of large factory interview complete test sites, information and my series of articles.

Json.stringify is a frequently used method that converts JavaScript values and objects into strings. Such as:

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

But there are many things wrong with JS, and this function is no exception. We might imagine that a function called “stringify “always returns a string…… But it didn’t!

For example, if you try stringify undefined, it returns undefined instead of a string.

JSON.stringify(undefined);
// => undefined
Copy the code

Next, I will talk about it in two parts:

  • listJSON.stringifyThe case where no string is returned
  • How can we avoid these pitfalls

What timeJSON.stringifyDoes not return a string?

Undefined, arbitrary functions, and symbol values are ignored during serialization (when appearing in an attribute value of a non-array object) or converted to NULL (when appearing in an array). Function undefined returns undefined when converted separately.

Executing this method on objects that contain circular references (objects that refer to each other in an infinite loop) throws an error

I think it’s surprising that json.stringify can return something other than a string. But there are six cases in which it can return undefined:

  1. Try to pair at the topundefinedSerialization, returnsundefined.
JSON.stringify(undefined);
// => undefined
Copy the code
  1. Attempts to serialize the function also returnundefined. This is true for regular functions, arrow functions, asynchronous functions, and generator functions.
JSON.stringify(function foo() {});
// => undefined

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

function bar() {}
bar.someProperty = 123;
JSON.stringify(bar);
// => undefined
Copy the code
  1. Attempts to serialize symbol are also returnedundefined.
JSON.stringify(Symbol("computers were a mistake"));
// => undefined
Copy the code
  1. In the browser, attempts to serialize are deprecated document.allAlso returnsundefined.
// => undefined
Copy the code

This only affects browsers, because document.all is not available in other environments, such as Node.

  1. withtoJSONThe objects of the function will be run instead of trying to serialize them normally. But if thetoJSONReturn one of the above values and attempting to serialize it at the top level will resultJSON.stringifyreturnundefined.
JSON.stringify({ toJSON: () => undefined });
// => undefined

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

JSON.stringify({ toJSON: () => Symbol("heya") });
// => undefined
Copy the code
  1. You can pass a second argument, called “replacer”, which changes the serialization logic. If this function returns one of these values for the top level,JSON.stringifyWill returnundefined.
JSON.stringify({ ignored: true }, () => undefined);
// => undefined

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

It’s important to note that many of these things really only affect top-level serialization. For example, json.stringify ({foo: undefined}), which returns the string “{}”, is not surprising.

I also want to mention that TypeScript type definitions are incorrect here. For example, the following code types can be verified by:

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

In Part 2, we’ll discuss how to update TypeScript definitions to ensure they are correct.

Json.stringify may also encounter problems that cause it to throw an error. Under normal circumstances, four things happen:

  1. A circular reference causes a type error to be thrown.
const b = { a };
a.b = b;

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

Note that these error messages may differ from browser to browser; for example, Firefox’s error message is different from Chrome’s.

  1. BigInts can’t use JSON.stringifySerialization, which also results in a 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
Copy the code
  1. withtoJSONThe object of the function will be run. If these functions throw an error, it will bubble up to the caller.
const obj = { foo: "ignored", toJSON() { throw new Error("Oh no!" ); }}; JSON.stringify(obj); // => Error: Oh no!Copy the code
  1. You can pass a second argument calledreplacer. If this function throws an error, it will bubble.
JSON.stringify({}, () => { throw new Error("Uh oh!" ); }); // => Error: Uh oh!Copy the code

Now that we’ve seen that json.stringify doesn’t return a string, let’s look at how to avoid these problems.

How to avoid these problems

There is no general approach to how to resolve these defects, so here are some common ones.

Handling circular references

From personal experience, json.stringify is the most error-prone when passing circular references. If this is a common problem for you, I recommend the JSON-stringify-safe package, which handles this situation well.

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 ~]"}}'
Copy the code

encapsulation

You may want to wrap json.stringify with your own custom functions. You can decide what you want it to do. Should mistakes come up? What if json. stringify returns undefined?

For example, Signal Desktop has a function called reallyJsonStringify, which always returns a string for debugging. Like this

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); }}Copy the code

A description of TypeScript types

If you already use TypeScript, you may be surprised to learn that TypeScript’s official definition of json.stringify is incorrect here. They actually look like this:

// JSON {//... stringify(value: any): string; }Copy the code

Unfortunately, this is a long-standing problem and there is no perfect solution.

You can try tinkering with json.stringify’s type, but each solution has certain drawbacks. I recommend defining your own wrappers with custom types and. For example, the template for Signal Desktop’s reallyJsonStringify:

function reallyJsonStringify(value: unknown): string {
  // ...
Copy the code

conclusion

  • JSON.stringifySometimes it comes backundefinedInstead of a string
  • JSON.stringifySometimes an error is thrown
  • We can solve this problem by wrapping functions in different ways

Hopefully this article has given you a more complete understanding of json.stringify.

I’m a dishwasher, and I’ll see you next time.


The bugs that may exist after code deployment cannot be known in real time. In order to solve these bugs, I spent a lot of time on log debugging. Incidentally, I recommend a good BUG monitoring tool for youFundebug.

Original text: evanhahn.com/when-string…

communication

Have a dream, have dry goods, wechat search [big Move the world] pay attention to this in the early morning is still in the bowl washing wisdom.

In this paper, making github.com/qq449245884… Has been included, a line of large factory interview complete test sites, information and my series of articles.