[譯] JavaScript 中 JSON.stringify 的二八法則

JavaScript 中 JSON.stringify 的二八法則

函數 JSON.stringify() 是一個把 JavaScript object 轉換成 JSON 的標準方法。不少 JavaScript 框架在底層都使用了 JSON.stringify(),例如:Express' res.json()Axios' post()Webpack stats 都在底層調用了 JSON.stringify()。這篇文章會提供一個實用的、包含異常狀況的 JSON.stringify() 的概述。javascript

開始

幾乎全部現代的 JavaScript 運行環境都支持 JSON.stringify()。甚至 IE 瀏覽器自從 IE8 起就支持JSON.stringify()。下面是一個把普通的 object 轉換成 JSON 的例子:html

const obj = { answer: 42 };

const str = JSON.stringify(obj);
str; // '{"answer":42}'
typeof str; // 'string'
複製代碼

如你所見,下面的例子是 JSON.stringify()JSON.parse() 一塊兒使用的。這種寫法能夠用來深拷貝 JavaScript 對象前端

const obj = { answer: 42 };
const clone = JSON.parse(JSON.stringify(obj));

clone.answer; // 42
clone === obj; // false
複製代碼

錯誤和邊界處理

若是 JSON.stringify() 的參數是 cyclical object,則會拋出一個錯誤。也就是說,若是對象 obj 有一個屬性,這個屬性的值是 obj 自己,那麼 JSON.stringify() 會拋出一個錯誤。java

const obj = {};
// 循環 object 指向它自身
obj.prop = obj;

// 拋出 "TypeError: TypeError: Converting circular structure to JSON"
JSON.stringify(obj);
複製代碼

這是 JSON.stringify() 惟一拋出異常的場景,除非你使用自定義的 toJSON() 函數或者使用替代函數(replacer)。然而即使這樣,你也仍是得把 JSON.stringify() 包在 try/catch 裏調用,由於循環 objects 仍是可能會出現。android

還有不少邊界場景 JSON.stringify() 不會拋出異常,但其結果可能不如你所想。好比,JSON.stringify() 會把 NaNInfinity 轉換成 nullwebpack

const obj = { nan: parseInt('not a number'), inf: Number.POSITIVE_INFINITY };

JSON.stringify(obj); // '{"nan":null,"inf":null}'
複製代碼

JSON.stringify() 也會把屬性值爲函數或者 undefined 的內容幹掉:ios

const obj = { fn: function() {}, undef: undefined };

// 空 object `JSON.stringify()` 過濾 functions 和 `undefined`。
JSON.stringify(obj); // '{}'
複製代碼

優化輸出

JSON.stringify() 的第一個參數是要被序列化成 JSON 的 object。實際上 JSON.stringify() 接受 3 個參數,第三個參數 spaces(譯註:空隙)。參數 spaces 用來將 JSON 格式化輸出成方便閱讀的格式。git

參數 spaces 能夠是 string 或 number。若是 spaces 不是 undefined,那麼JSON.stringify() 則會把 JSON 中的每個 key 單獨做爲一行輸出,而且加上 spaces 的前綴。github

const obj = { a: 1, b: 2, c: 3, d: { e: 4 } };

// '{"a":1,"b":2,"c":3,"d":{"e":4}}'
JSON.stringify(obj);

// {
// "a": 1,
// "b": 2,
// "c": 3,
// "d": {
// "e": 4
// }
// }
JSON.stringify(obj, null, ' ');

// 使用 2 個空格來格式化 JSON 輸出。和上面的例子等價。
JSON.stringify(obj, null, 2);
複製代碼

把參數 spaces 做爲字符串使用時,雖然在實際場景中大可能是使用空格,但其實不限制必須全是空格。例如:web

// {
// __"a": 1,
// __"b": 2,
// __"c": 3,
// __"d": {
// ____"e": 4
// __}
// }
JSON.stringify(obj, null, '__');
複製代碼

Replacers

JSON.stringify() 的第二個參數是 replacer 函數。在上面的例子中,replacernull。JavaScript 針對 object 中的每個 key/value 對都會調用 replacer 函數,使用函數的返回值做爲屬性的值。例如:

const obj = { a: 1, b: 2, c: 3, d: { e: 4 } };

// `replacer` 使每一個數字的值加 1。輸出:
// '{"a":2,"b":3,"c":4,"d":{"e":5}}'
JSON.stringify(obj, function replacer(key, value) {
  if (typeof value === 'number') {
    return value + 1;
  }
  return value;
});
複製代碼

替代函數(譯註:replacer)在過濾敏感詞的場景很是有用。例如,假設你想過濾全部包含 'password' 及 'password' 子字符串的 keys

const obj = {
  name: 'Jean-Luc Picard',
  password: 'stargazer',
  nested: {
    hashedPassword: 'c3RhcmdhemVy'
  }
};

// '{"name":"Jean-Luc Picard","nested":{}}'
JSON.stringify(obj, function replacer(key, value) {
  // 這個函數會被調用 5 次。 `key` 等於:
  // '', 'name', 'password', 'nested', 'hashedPassword'
  if (key.match(/password/i)) {
    return undefined;
  }
  return value;
});
複製代碼

函數 toJSON()

JSON.stringify() 函數會遍歷 object 尋找含有 toJSON() 函數的屬性。若是它找到了 toJSON() 函數,JSON.stringify() 會調用 toJSON() 函數,並使用其返回值做爲替代。例如:

const obj = {
  name: 'Jean-Luc Picard',
  nested: {
    test: 'not in output',
    toJSON: () => 'test'
  }
};

// '{"name":"Jean-Luc Picard","nested":"test"}'
JSON.stringify(obj);
複製代碼

函數 toJSON() 能夠返回任何值,包括對象、原始類型的值,甚至 undefined。若是 toJSON() 返回 undefinedJSON.stringify() 會忽略這個屬性。

許多 JavaScript 模塊使用 toJSON() 這一特性來保證複雜的對象能被正確的序列化。好比 Mongoose documentsMoment objects

後續

函數 JSON.stringify() 是 JavaScript 基礎的核心。許多庫和框架在底層都使用了它,因此對 JSON.stringify() 的紮實理解,能夠幫助更好的學習你感興趣的 npm 模塊。好比,針對你的 Express REST API,能夠借用自定義 toJSON() 函數的能力來處理原生的 Date 類,以此實現一個日期格式化 的替代方案,或者,當使用 Axios 發送 HTTP 請求時,確保客戶端的循環對象能被正確的轉換成 JSON。(譯註:帕累托法則即 80/20 Rule,通常指 20% 的輸入,決定 80% 的結果的現象。)

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索