作者: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
:
- 試圖在頂層對
undefined
進行序列化,會返回undefined
。
JSON.stringify(undefined);
// => undefined
- 嘗試序列化函式也會返回
undefined
。對於常規函式、箭頭函式、非同步函式和生成器函式都是如此。
JSON.stringify(function foo() {});
// => undefined
JSON.stringify(() => {});
// => undefined
function bar() {}
bar.someProperty = 123;
JSON.stringify(bar);
// => undefined
- 嘗試序列化symbol 也會返回
undefined
。
JSON.stringify(Symbol("computers were a mistake"));
// => undefined
- 在瀏覽器中,試圖序列化被廢棄的
document.all
也會返回undefined
。
// => undefined
這隻影響到瀏覽器,因為document.all在其他環境中是不可用的,比如Node。
- 帶有
toJSON
函式的物件將被執行,而不是試圖正常地序列化它們。但是如果toJSON
返回上面的一個值,試圖在頂層序列化它將導致JSON.stringify
返回undefined
。
JSON.stringify({ toJSON: () => undefined });
// => undefined
JSON.stringify({ ignored: true, toJSON: () => undefined });
// => undefined
JSON.stringify({ toJSON: () => Symbol("heya") });
// => undefined
- 你可以傳遞第二個引數,稱為 "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
也可能遇到問題,導致它丟擲一個錯誤。在正常情況下,有四種情況會發生:
- 迴圈引用會導致丟擲一個型別錯誤。
const b = { a };
a.b = b;
JSON.stringify(a);
// => TypeError: cyclic object value
注意,這些錯誤訊息在不同瀏覽器可能提示是不樣的,例如,Firefox 的錯誤資訊與Chrome的不同。
- 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
- 帶有
toJSON
函式的物件將被執行。如果這些函式丟擲錯誤,它將冒泡到呼叫者。
const obj = {
foo: "ignored",
toJSON() {
throw new Error("Oh no!");
},
};
JSON.stringify(obj);
// => Error: Oh no!
- 你可以傳遞第二個引數,稱為
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... 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。