JSON.stringify
是咱們在平常開發中常常會用到的一個函數。例如:用來處理 POST 請求中的 body 參數,將 object
對象存儲到 localStorage
裏面,甚至能夠用來實現一個簡單的深拷貝。javascript
然而,就是這麼一個常見的 JS 官方函數,卻有着一些少有人知道的小祕密。今天,就讓咱們來一探究竟吧。html
這個可能有很多人都知道。JSON.stringify
接收一個參數來美化輸出結果,讓其更加可讀。java
默認狀況下,JSON.stringify
會把序列化結果輸出爲一行。但當提供了第三個參數 space
的時候,JSON.stringify
會爲每個鍵值單起一行,而且鍵名key
前面附加上 space
前綴。web
space
能夠是數字或者字符串。json
const obj = { a: 1, b: { c: 2 } }
JSON.stringify(obj)
// "{"a":1,"b":{"c":2}}"
JSON.stringify(obj, null, 2)
// "{
// "a": 1,
// "b": {
// "c": 2
// }
// }"
JSON.stringify(obj, null, '**')
// "{
// **"a": 1,
// **"b": {
// ****"c": 2
// **}
// }"
複製代碼
replacer
相信必定有同窗很好奇上面第二個參數 null
是幹嗎的。莫要着急,且聽我細細講來。app
JSON.stringify
的第二個參數咱們稱之爲 replacer
。能夠是列表或者函數。ide
replacer
是列表當 replacer
是列表的時候,它的做用就像是白名單同樣,最終結果中只會包含 key
在列表中的 key: value
。函數
const user = {
name: 'Hopsken',
website: 'https://hopsken.com',
password: 'SUPER_SECRET',
}
JSON.stringify(user, ['name', 'website'], 2)
// "{
// "name": "Hopsken",
// "website": "https://hopsken.com"
// }"
複製代碼
值得一提的是,最終結果中的鍵名排列會遵守列表中的前後順序。ui
JSON.stringify(user, ['website', 'name'], 2)
// "{
// "website": "https://hopsken.com",
// "name": "Hopsken"
// }"
// 你甚至能夠這樣
const config = { ... }
JSON.stringify(config, Object.keys(config).sort(), 2)
複製代碼
replacer
是函數當replacer
是函數的時候,JS 會在序列化是對每一個鍵值對調用這個函數,而且使用函數的返回值做爲 key
對應的 value
。this
const obj = { a: 1, b: { c: 2 } }
JSON.stringify(obj, (key, value) => {
if (typeof value === 'number') {
return value + 1;
}
return value;
}, 2)
// "{
// "a": 2,
// "b": {
// "c": 3
// }
// }"
複製代碼
若是返回值爲 undefined
,那麼最終結果中將忽略該值。(這是預期的行爲,由於 JSON 標準格式中並無 undefined
這個值)。
const user = {
name: 'Hopsken',
password: 'SUPER_SECRET',
}
JSON.stringify(user, (key, value) => {
if (key.match(/password/i)) {
return undefined;
}
return value;
}, 2)
// "{
// "name": "Hopsken"
// }"
複製代碼
JSON.stringify
的第三個祕密!當 JSON.stringify()
在嘗試對一個對象進行序列化時,會先遍歷這個對象,檢查對象是否存在 toJSON()
這個屬性。若是有的話,JSON.stringify()
將會對這個函數返回的值進行序列化,而非原來的對象。
簡單來講,toJSON()
方法定義了什麼值將被序列化。
經過 toJSON()
這個方法,咱們能夠本身去控制 JSON.stringify()
的行爲。
舉個例子:
const movie = {
title: '讓子彈飛',
year: 2010,
stars: new Set(['周潤發', '姜文', '葛優']),
toJSON() {
return {
name: `${this.title} ${this.year}`,
actors: [... this.stars]
}
}
}
JSON.stringify(movie, null, 2)
// "{
// "name": "讓子彈飛 2010",
// "actors": [
// "周潤發",
// "姜文",
// "葛優"
// ]
// }"
複製代碼
上面的例子,咱們就用了 toJSON
屬性來讓 JSON.stringify()
來支持序列化 Set
類型數據。
值得一提的是,toJSON
中的 this
指向的是當前層級的對象,做用域也只在當前層級對象上。
const user = {
name: 'Hospken',
wechat: {
name: 'FEMinutes',
toJSON() {
return `WX: ${this.name}`;
},
}
}
JSON.stringify(user, null, 2)
// "{
// "name": "Hospken",
// "wechat": "WX: FEMinutes"
// }"
複製代碼
能夠看到,wechat.toJSON()
只做用在了 wechat
這個屬性上。
JSON.stringify
?那咱們知道,JS 裏面列表也是 object
。那按理說 JSON.stringify
應該也能夠用在列表上。
首先,咱們先來試一下默認參數下的結果。
const arr = ["apple", "orange", "banana"]
JSON.stringify(arr)
// "["apple","orange","banana"]"
複製代碼
一切正常。
再來試一下 space
參數:
const arr = ["apple", "orange", "banana"]
JSON.stringify(arr, null, 2)
// "[
// "apple",
// "orange",
// "banana"
// ]"
複製代碼
Perfect!
那 replacer
呢?
const arr = ["apple", "orange", "banana"]
JSON.stringify(arr, (key, value) => {
return `one ${value}`
}, 2)
// ""one apple,orange,banana""
複製代碼
涼涼,彷佛不行啊。。。看上去 replacer
函數只執行了一次,而不是預期中的三次。可是,原本 JSON.stringify()
也不是這麼用的,出現預期之外的結果也是正常的。
可是不要緊,我們還有 toJSON()
啊!讓咱們試一下:
const arr = ["apple", "orange", "banana"]
arr.toJSON = function() {
return this.slice().map(val => `one ${val}`)
}
JSON.stringify(arr, null, 2)
// "[
// "apple",
// "orange",
// "banana"
// ]"
複製代碼
這樣我們就經過 toJSON()
實現了 replacer
的做用。豈不美哉。
那麼今天的探祕就到這裏啦。篇幅限制,關於 JSON.stringify()
對邊界狀況的處理(如循環引用)我們就不詳細展開講啦。
感謝各位的觀看~我們下期再見~
ECMAScript 2015 (6th Edition, ECMA-262)