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