JavaScript JSON
JSON 對象包含兩個(gè)方法: 用于解析 JavaScript Object Notation (JSON) 的 parse() 方法,以及將對象/值轉(zhuǎn)換為 JSON字符串的 stringify() 方法。除了這兩個(gè)方法, JSON 這個(gè)對象本身并沒有其他作用,也不能被調(diào)用或者作為構(gòu)造函數(shù)調(diào)用。
JavaScript 內(nèi)置的 JSON對象
用于處理 JSON
。
JSON(JavaScript Object Notation)是一種帶有格式的文本,JavaScript 中的 JSON對象
用于處理這種文本。
JSON 對象只提供了兩個(gè)方法,一個(gè)用于序列化 JSON ,一個(gè)用于反序列化 JSON 。
這里的序列化可以理解成將JavaScirpt對象轉(zhuǎn)換成JSON
,反序列化則是將JSON轉(zhuǎn)換成JavaScript對象
。
1. JSON.parse
JSON.parse
用于解析 JSON 格式的字符串,將 JSON 轉(zhuǎn)化成 JavaScript 對象。
JSON.parse(JSON字符串, 處理函數(shù));
第一個(gè)參數(shù)是要轉(zhuǎn)換成對象的 JSON 字符串,第二個(gè)參數(shù)可以不傳遞/
var str = '{ "name": "baba", "age": 12, "info": { "locate": "浙江" } }';
var user = JSON.parse(str);
console.log(user); // 輸出一個(gè) JavaScript 對象
傳遞給 JSON.parse 方法的字符串要符合 JSON 標(biāo)準(zhǔn),否則會報(bào)錯。
第二個(gè)參數(shù)非常有趣,傳入的是一個(gè)函數(shù),這個(gè)函數(shù)會在每個(gè) JSON 屬性被解析的時(shí)候調(diào)用,同時(shí)會傳遞屬性名和屬性值給函數(shù)作為參數(shù),傳入?yún)?shù)的返回值會作為當(dāng)前遍歷到的屬性的新值。
var str = '{ "name": "baba", "age": 12, "info": { "locate": "浙江" } }';
var user = JSON.parse(str, function(key, value) {
console.log(key, value);
return value;
});
可以發(fā)現(xiàn)上述例子打印的最后一項(xiàng),屬性名是可以空字符串,屬性值是解析完的結(jié)果。
修改一下例子,將返回值改成一個(gè)固定的值。
var str = '{ "name": "baba", "age": 12, "info": { "locate": "浙江" } }';
var user = JSON.parse(str, function(key, value) {
console.log(key, value);
return '強(qiáng)行修改值';
});
觀察輸出后,可以發(fā)現(xiàn)所有屬性都被遍歷了,并且賦值成功,但是最終 user
也變成了返回的字符串。
這是因?yàn)楫?dāng)解析完成后,傳入的函數(shù)會被最后調(diào)用一次,傳遞進(jìn)來的值就是最終 JSON.parse 的返回值,所以對其修改后,會影響到的最終結(jié)果。
var str = '{ "name": "baba", "age": 12, "info": { "locate": "浙江" } }';
var user = JSON.parse(str, function(key, value) {
console.log(key, value);
if (key === '') {
return value;
}
return '強(qiáng)行修改值';
});
對傳遞過來的屬性名為空字符串 ''
進(jìn)行單獨(dú)處理即可避免這種特殊情況。
業(yè)務(wù)邏輯中很少會用第二個(gè)參數(shù)來處理解析內(nèi)容。
2. JSON.stringify
JSON.stringify 用于將JavaScript對象轉(zhuǎn)換成 JSON 格式的字符串。
JSON.stringify(JavaScript對象, 處理函數(shù), 縮進(jìn)空格字符數(shù)量);
第一個(gè)參數(shù)是需要轉(zhuǎn)換成 JSON 字符串的對象。
第二個(gè)參數(shù)可以是個(gè)函數(shù),也可以是個(gè)數(shù)組。
如果是函數(shù),則每一個(gè)屬性在處理的時(shí)候就被調(diào)用這個(gè)函數(shù),同時(shí)屬性名和屬性值作為參數(shù)傳遞給這個(gè)函數(shù),并且函數(shù)的返回值作為這個(gè)處理屬性的值。
如果是數(shù)組,則只有屬性名在數(shù)組中的屬性才會被處理,不傳遞則默認(rèn)處理整個(gè)對象。
如果第二個(gè)參數(shù)傳遞 null ,也就是不做特殊處理,在使用到第三個(gè)參數(shù)的時(shí)候,第二個(gè)參數(shù)會傳遞 null 。
第三個(gè)參數(shù)可以傳遞數(shù)字,也可以傳遞字符串,傳遞了這個(gè)參數(shù)會對結(jié)果做格式化,具有一定的格式,參數(shù)的值決定格式化的樣式。
如果是數(shù)字,則使用對應(yīng)長度的空格來縮進(jìn),長度 1 到 10 ,比 1 小則表示不縮進(jìn)。
如果是字符串,則會使用傳入的字符串進(jìn)行縮進(jìn),傳入的字符串長度超過 10 ,則會截取前 10 個(gè)作為縮進(jìn)字符。
var user = {
name: '小明',
age: 14,
skill: ['HTML', 'Java'],
};
var json = JSON.stringify(user);
console.log(json);
// 輸出:{"name":"小明","age":14,"skill":["HTML","Java"]}
第二個(gè)參數(shù)用起來和 parse 方法的第二個(gè)參數(shù)類似。
var user = {
name: '小明',
age: 14,
skill: ['HTML', 'Java'],
};
var json = JSON.stringify(user, function(key, value) {
console.log(key, vlue);
return value;
});
console.log(json);
根據(jù)上述例子可以看到,先輸出的屬性為空字符串,屬性值為被處理對象,所以如果不想操作原對象,需要做特殊處理。
var user = {
name: '小明',
age: 14,
skill: ['HTML', 'Java'],
};
var json = JSON.stringify(user, function(key, value) {
if (key === '') {
return value;
}
return '我是處理過的值';
});
console.log(json);
這樣處理后,最終處理完的 JSON 字符串的屬性值都是函數(shù)的返回值了。
第三個(gè)參數(shù)會在做一些工具類調(diào)試的時(shí)候常用到。
var obj = [
{
path: '/',
component: 'function() {}',
children: [
{
path: 'note',
component: 'function() {}',
},
{
path: 'friends',
component: 'function() {}',
}
]
},
{
path: '*',
component: 'function() {}',
}
];
var json1 = JSON.stringify(obj, null);
var json2 = JSON.stringify(obj, null, 2);
var json3 = JSON.stringify(obj, null, '*-*');
console.log(json1); // 沒有格式
console.log(json2); // 使用兩個(gè)空格控制的縮進(jìn)
console.log(json3); // 使用 *-* 控制的縮進(jìn)
傳入?yún)?shù)后就會將處理后的 JSON 字符串進(jìn)行格式化,縮進(jìn)部分根據(jù)傳入的參數(shù)值決定。
3. 其他注意點(diǎn)
3.1 深拷貝
可以配合 JSON 的兩個(gè)方法,對對象進(jìn)行深拷貝。
var obj = {prop: 'value'};
var newObj = JSON.parse(JSON.stringify(obj));
newObj.prop = 'new value';
console.log(obj);
console.log(newObj);
根據(jù)結(jié)果可以看到新的對象修改,沒有影響到原對象,兩者之間不存在引用關(guān)系。
3.2 序列化規(guī)則
使用 JSON.stringify 有些內(nèi)置規(guī)則。
- 如果對象中存在包裝對象,則在轉(zhuǎn)換過程中會變成原始值。
var obj = {
string: new String('A promise is a promise.'),
number: new Number(996),
};
var result = JSON.stringify(obj);
console.log(result); // 輸出:"{"string":"A promise is a promise.","number":996}"
- 如果轉(zhuǎn)換的對象或者對象下的屬性存在 toJSON 方法,那么這個(gè)方法的返回值會作為轉(zhuǎn)換結(jié)果。
var user = {
nickname: 'joker',
toJSON: function() {
return 'hahahahahahaha';
},
}
var result = JSON.stringify(user);
console.log(result); // 輸出:"hahahahahahaha"
可以看到結(jié)果為 toJSON 方法的返回值。
- 除了數(shù)組以外的對象,轉(zhuǎn)換結(jié)果順序?yàn)殡S機(jī)。
var obj = {
b: 2,
c: 3,
a: 1,
};
如以上對象,轉(zhuǎn)換的結(jié)果有可能是以下情況中的一種:
"{"a":1,"b":2,"c":3}"
"{"a":1,"c":3,"b":2}"
"{"b":2,"a":1,"c":3}"
"{"b":2,"c":3,"a":1}"
"{"c":3,"b":2,"a":1}"
"{"c":3,"a":1,"b":2}"
- undefined、ES6 中的 symbol 值、函數(shù)在轉(zhuǎn)換過程中都會被忽略,當(dāng)然函數(shù)如果具有 toJSON 方法依然會優(yōu)先選擇 toJSON 方法的結(jié)果。
var fn = function() {};
fn.toJSON = function() {return '我是函數(shù)'};
var result = JSON.stringify({
a: fn,
b: Symbol(1),
c: undefined,
d: function() {},
});
console.log(result);
- 存在循環(huán)引用,則會報(bào)錯
var obj1 = {
prop1: 1,
};
var obj2 = {
prop1: 1,
};
obj1.prop2 = obj2;
obj2.prop2 = obj1;
JSON.stringify(obj1); // TypeError: Converting circular structure to JSON
兩個(gè)對象相互引用之后,進(jìn)行系列化就會拋出錯誤。
- 在 ES6 中,symbol 可以作為對象的屬性值,但在處理的時(shí)候都會被忽略。
var symbol = Symbol();
var obj = {
prop1: 'value1',
[symbol]: 'value2',
};
console.log(obj);
var result = JSON.stringify(obj);
console.log(result); // 輸出:{"prop1":"value1"}
- null、正負(fù) Infinity、NaN 在序列化時(shí)都會被當(dāng)作 null 。
var obj = {
null: null,
infinity1: +Infinity,
infinity2: -Infinity,
NaN: NaN,
};
var result = JSON.stringify(obj);
console.log(result); // 輸出:{"null":null,"infinity1":null,"infinity2":null,"NaN":null}
4. 小結(jié)
JSON 幾乎是目前前后端交互最常用的數(shù)據(jù)格式,所以 JSON 對象使用的頻率也很高。
在使用 JSON.parse
反序列化的時(shí)候,如果 JSON 格式不符合規(guī)范,是會報(bào)錯的,日常開發(fā)中建議封裝一層 JSON 的方法,將錯誤集中處理,方便定位與上報(bào)錯誤。