之前在看公眾號(hào)的時(shí)候發(fā)現(xiàn)了對(duì)JSON.stringify的這篇文章 覺得很全面 所以自己簡單總結(jié)了一寫知識(shí)點(diǎn)??
- 對(duì)于 undefined、任意的函數(shù)以及 symbol 三個(gè)特殊的值分別作為對(duì)象屬性的值、數(shù)組元素、單獨(dú)的值時(shí) JSON.stringify()將返回不同的結(jié)果。
undefined、任意的函數(shù)以及 symbol 作為對(duì)象屬性值時(shí) JSON.stringify() 將跳過(忽略)對(duì)它們進(jìn)行序列化
undefined、任意的函數(shù)以及 symbol 作為數(shù)組元素值時(shí),JSON.stringify() 會(huì)將它們序列化為 null
undefined、任意的函數(shù)以及 symbol 被 JSON.stringify() 作為單獨(dú)的值進(jìn)行序列化時(shí)都會(huì)返回 undefined
- 非數(shù)組對(duì)象的屬性不能保證以特定的順序出現(xiàn)在序列化后的字符串中。也就是說序列化前后的屬性的順序可能不一致
原因:正如我們?cè)诘谝惶匦运f,JSON.stringify() 序列化時(shí)會(huì)忽略一些特殊的值,所以不能保證序列化后的字符串還是以特定的順序出現(xiàn)(數(shù)組除外)
- 轉(zhuǎn)換值如果有 toJSON() 函數(shù),該函數(shù)返回什么值,序列化結(jié)果就是什么值,并且忽略其他屬性的值。
JSON.stringify({
say: "hello JSON.stringify",
toJSON: function() {
return "today i learn";
}
})
// "today i learn"
- JSON.stringify() 將會(huì)正常序列化 Date 的值。
JSON.stringify({ now: new Date() });
// "{"now":"2019-12-08T07:42:11.973Z"}"
實(shí)際上 Date 對(duì)象自己部署了 toJSON() 方法(同 Date.toISOString()),因此 Date 對(duì)象會(huì)被當(dāng)做字符串處理。
- NaN 和 Infinity 格式的數(shù)值及 null 都會(huì)被當(dāng)做 null。
JSON.stringify(NaN)
// "null"
JSON.stringify(null)
// "null"
JSON.stringify(Infinity)
// "null"
- 布爾值、數(shù)字、字符串的包裝對(duì)象在序列化過程中會(huì)自動(dòng)轉(zhuǎn)換成對(duì)應(yīng)的原始值
JSON.stringify([new Number(1), new String("false"), new Boolean(false)]);
// "[1,"false",false]"
- 其他類型的對(duì)象,包括 Map/Set/WeakMap/WeakSet,僅會(huì)序列化可枚舉的屬性。
// 不可枚舉的屬性默認(rèn)會(huì)被忽略:
JSON.stringify(
Object.create(
null,
{
x: { value: 'json', enumerable: false },
y: { value: 'stringify', enumerable: true }
}
)
);
// "{"y":"stringify"}"
- 我們都知道實(shí)現(xiàn)深拷貝最簡單粗暴的方式就是序列化:JSON.parse(JSON.stringify()),這個(gè)方式實(shí)現(xiàn)深拷貝會(huì)因?yàn)樾蛄谢闹T多特性從而導(dǎo)致諸多的坑點(diǎn):比如現(xiàn)在我們要說的循環(huán)引用問題
對(duì)包含循環(huán)引用的對(duì)象(對(duì)象之間相互引用,形成無限循環(huán))執(zhí)行此方法,會(huì)拋出錯(cuò)誤。
- 所有以 symbol 為屬性鍵的屬性都會(huì)被完全忽略掉,即便 replacer 參數(shù)中強(qiáng)制指定包含了它們。
JSON.stringify({ [Symbol.for("json")]: "stringify" }, function(k, v) {
if (typeof k === "symbol") {
return v;
}
})
// undefined
JSON.stringify 的第二個(gè)和第三個(gè)參數(shù)
- 第二個(gè)參數(shù) replacer
replacer 參數(shù)有兩種形式,可以是一個(gè)函數(shù)或者一個(gè)數(shù)組。作為函數(shù)時(shí),它有兩個(gè)參數(shù),鍵(key)和值(value),函數(shù)類似就是數(shù)組方法 map、filter 等方法的回調(diào)函數(shù),對(duì)每一個(gè)屬性值都會(huì)執(zhí)行一次該函數(shù)。如果 replacer 是一個(gè)數(shù)組,數(shù)組的值代表將被序列化成 JSON 字符串的屬性名。序列化之后的字符串只包含數(shù)組中給出的屬性名。
作用:
第二個(gè)參數(shù) replacer 非常強(qiáng)大, replacer 作為函數(shù)時(shí),我們可以打破九大特性的大多數(shù)特性,我們直接來看代碼吧
const data = {
a: "aaa",
b: undefined,
c: Symbol("dd"),
fn: function() {
return true;
}
};
// 不用 replacer 參數(shù)時(shí)
JSON.stringify(data);
// "{"a":"aaa"}"
// 使用 replacer 參數(shù)作為函數(shù)時(shí)
JSON.stringify(data, (key, value) => {
switch (true) {
case typeof value === "undefined":
return "undefined";
case typeof value === "symbol":
return value.toString();
case typeof value === "function":
return value.toString();
default:
break;
}
return value;
})
// "{"a":"aaa","b":"undefined","c":"Symbol(dd)","fn":"function() {\n return true;\n }"}"
需要注意的是,replacer 被傳入的函數(shù)時(shí),第一個(gè)參數(shù)不是對(duì)象的第一個(gè)鍵值對(duì),而是空字符串作為 key 值,value 值是整個(gè)對(duì)象的鍵值對(duì):
const data = {
a: 2,
b: 3,
c: 4,
d: 5
};
JSON.stringify(data, (key, value) => {
console.log(value);
return value;
})
// 第一個(gè)被傳入 replacer 函數(shù)的是 {"":{a: 2, b: 3, c: 4, d: 5}}
// {a: 2, b: 3, c: 4, d: 5}
// 2
// 3
// 4
// 5
- 第三個(gè)參數(shù)
space 參數(shù)用來控制結(jié)果字符串里面的間距
如果是一個(gè)數(shù)字, 則在字符串化時(shí)每一級(jí)別會(huì)比上一級(jí)別縮進(jìn)多這個(gè)數(shù)字值的空格(最多 10 個(gè)空格);
如果是一個(gè)字符串,則每一級(jí)別會(huì)比上一級(jí)別多縮進(jìn)該字符串(或該字符串的前 10 個(gè)字符)。
eg:
const tiedan = {
name: "彈鐵蛋同學(xué)",
describe: "今天在學(xué) JSON.stringify()",
emotion: "like shit"
};
JSON.stringify(tiedan, null, "??");
// 接下來是輸出結(jié)果
// "{
// ??"name": "彈鐵蛋同學(xué)",
// ??"describe": "今天在學(xué) JSON.stringify()",
// ??"emotion": "like shit"
// }"
JSON.stringify(tiedan, null, 2);
// "{
// "name": "彈鐵蛋同學(xué)",
// "describe": "今天在學(xué) JSON.stringify()",
// "emotion": "like shit"
// }"