你不知道的 JSON.stringfy

http://thecodebarbarian.com/t...

寫在前面

JSON.stringfy()是將一個 JavaScript 對象轉化爲 JSON 格式字符串的標準方式。許多 JavaScript 框架在其內部,都會使用 JSON.stringify()Expressres.json()Axiospost,以及webpack stats,它們都調用了 JSON.stringify() 方法,幷包含錯誤案例。javascript

譯者注:這篇文章已經被翻譯過屢次了,可是我以爲寫的太好了,因此就再翻譯一次吧,看成是加深印象。html

簡單入門

全部的現代 JavaScript 運行時都支持 JSON.stringify(),甚至 IE8 都支持它。下面是一個將簡單對象轉化爲 JSON 的例子:java

const obj = { answer: 42 };

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

你可能常常看到 JSON.stringify()JSON.parse() 一塊兒配合使用的場景,就像下面的代碼同樣,這種模式是實現深拷貝的方式之一:webpack

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

clone.answer; // 42
clone === obj; // false

錯誤以及邊界用例

JSON.stringify() 在轉化對象存在循環引用時,會拋出錯誤。更簡單地說,就是若是一個對象有一個屬性指向它自己,JSON.stringify() 會拋出錯誤,好比:ios

const obj = {};
// 存在循環引用的對象,它指向它自己
obj.prop = obj;

// 會拋出 "TypeError: TypeError: Converting circular structure to JSON" 異常
JSON.stringify(obj);

這是 JSON.stringify() 會拋出異常的惟一狀況,除非你經過聲明自定義的 toJSON() 方法或者 replacer 函數。儘管如此,你仍然應該將 JSON.stringify() 包含在 try/catch 語句中,由於循環引用在實踐中十分常見。git

同時,一些邊界用例下,JSON.stringify() 並不會拋出錯誤,可是你可能卻指望它拋出錯誤。好比說,JSON.stringify() 會將 NaNInfinity 轉化爲 null:github

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

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

JSON.stringify() 也會直接省略那些值爲 functionsundefined 的屬性,以下:web

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

// 它會返回空對象
JSON.stringify(obj); // '{}'

格式化

JSON.stringify() 的第一個參數是被序列化爲 JSON 的對象。JSON.stringify() 實際上能夠接受 3 個參數,同時第三個參數被稱做 spacesspaces 參數被用於採用一種能夠提升可讀性的方式來格式化 JSON 字符串。express

你能夠傳遞類型爲 string 或者 numberspaces 參數。若是 spacesundefined,那麼 JSON.stringify() 會將每一個鍵值放到單獨的一行,同時爲其增長正確的縮進空格,好比:npm

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.stringify(obj, null, 2);

spaces 字符串不必定非要是空格,雖然一般咱們會使用空格,好比它也能夠是下劃線:

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

Replacers

JSON.stringify() 的第二個參數是 replacer 函數。在上文的例子中,它等於 null。JavaScript 會對對象中的每個鍵值對調用 replacer 函數,而後會使用其返回值,做爲格式化後的值,好比:

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

// 使每一個屬性的值遞增 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 的屬性:

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() 方法在遍歷對象的同時,也會關注那些擁有 toJSON() 方法的屬性。若是它發現 toJSON() 方法,JSON.stringify() 會調用它,而後將它的返回值替換格式化後的值,好比:

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 對象

最後

JSON.stringify() 是 JavaScript 中較核心的基礎方法。許多庫和框架在其內部都使用它,所以,深刻的理解它,能夠幫助你更好地使用你喜歡的 npm 模塊。好比,你能夠在 Express REST API 中利用 toJSON 方法來格式化原生 Date 類型,或者在 Axios 中,可以正確地經過 HTTP 請求發送包含循環引用的對象。


關注公衆號 全棧101,只談技術,不談人生

clipboard.png

相關文章
相關標籤/搜索