JSON語法能夠表示如下三種類型的值:javascript
null
, 可是不支持undefined
JSON不支持變量/函數/對象實例java
字符串編程
"Hello JSON"
數字json
66
布爾值數組
true
null數據結構
null
和JavaScript對比來看 ->編程語言
鍵值對的鍵能夠加引號也能夠不加, 若是加引號, 能夠加單引號也能夠加雙引號函數
// 最多見就這麼寫 const obj1 = { foo: 'bar', num: 66, status: true }; // 這麼寫也oconstet obj2 = { 'foo': 'bar', 'num': 66, 'status': true }; // 這麼寫也ok const obj1 = { "foo": "bar", "num": 66, "status": true };
鍵值對的鍵必須加雙引號(手寫JSON時必定要注意)this
對象沒有變量聲明, 由於JSON根本就沒有變量的概念(它不是一個編程語言)code
末尾沒有分號
{ "foo": "bar", "num": 66, "status": true }
和JavaScript類似, 對象能夠嵌套對象
{ "foo": "bar", "num": 66, "status": true, "baz": { "num": 88 } }
注, 同名屬性能夠在不一樣的對象中, 可是不能出如今同一個對象中
和JavaScript對比來看 ->
let arr = ['hello', 66, true];
一樣沒有變量聲明和結尾的分號, 同時注意字符串簡單值要加雙引號
["hello", 66, true]
數組和對象結合起來能夠構成複雜的集合, 好比students.json文件中多是這樣嬸兒的
[ { "name": "小明", "age": 10, "score": { "math": 88, "english": 99 } }, { "name": "小強", "age": 11, "score": { "math": 98, "english": 96 } } ]
看到以上同JavaScript的不一樣之處, 咱們能夠知道爲何說JSON是JavaScript的一個嚴格子集了吧
ECMAScript5定義了全局對象JSON, 用來解析JSON字符串
簡單來講, JSON對象有兩個方法
JSON.stringify()
: 把JavaScript對象序列化爲JSON字符串JSON.parse()
: 把JSON字符串解析爲原生JavaScript值const book = { name: 'Learn JavaScript in One Day', pages: 1 }; const jsonText = JSON.stringify(book); // 序列化 // "{"name":"Learn JavaScript in One Day","pages":1}" const parseText = JSON.parse(jsonText); // 解析 // {name: "Learn JavaScript in One Day", pages: 1}
默認狀況下, JSON.stringify()
輸出的JSON字符串不包含任何空格字符或縮進(是否是給咱們提供了一種將去除數據中無用的空白和縮進的方法呢).
在序列化JavaScript對象時, 全部函數及原型成員都會被有意忽略, 不體如今結果中. 此外, 值爲undefined
的任何屬性也都會被跳過. 結果中最終都是值爲有效JSON數據類型的實例屬性.
const book = { name: 'Learn JavaScript in One Day', pages: 1, foo: undefined }; const jsonText = JSON.stringify(book); // 序列化 // "{"name":"Learn JavaScript in One Day","pages":1}" // foo的值爲undefined, 所被忽略掉了
將JSON字符串直接傳遞給JSON.parse()
就能夠獲得相應的JavaScript值.
JSON.parse()
就是JSON.stringify()
的逆向操做. 將一個JavaScript值序列化以後再解析JSON.parse(JSON.stringify(foo))
和原來的foo
幾乎同樣.
注意, 爲何說是幾乎同樣呢?
這就提供了一種克隆對象和數組的方法😆
undefined
的屬性, 那麼在序列化的過程當中它會被篩掉;undefined
的項, 那麼在序列化過程當中undefined
的項會變成null
;const foo1 = { a: 1 }; JSON.parse(JSON.stringify(foo1)) === foo1; // false const foo2 = [1, 2]; JSON.parse(JSON.stringify(foo2)) === foo2; // false const foo3 = { a: 1, b: undefined }; JSON.parse(JSON.stringify(foo3)); // {a: 1} const foo4 = [1, true, undefined]; JSON.parse(JSON.stringify(foo4)); // [1, true, null] const foo5 = true; JSON.parse(JSON.stringify(foo5)) === foo5; // true
若是傳給JSON.parse()
的字符串不是有效的JSON, 該方法會拋出錯誤.
JSON.stringify()
除了要序列化的JavaScript對象外, 還能夠接收另外兩個參數, 這兩個參數用於指定以不一樣的方式序列化JavaScript對象.
第一個參數是個過濾器, 能夠是一個數組, 也能夠是一個函數;
第二個參數是一個選項, 表示是否在JSON字符串中保留縮進.
1.過濾結果
若是過濾器參數是數組, 那麼JSON.stringify()
的結果中將只包含數組中列出的屬性
const book = { name: 'Learn JavaScript in One Day', pages: 1 }; JSON.stringify(book, ['name']); // "{"name":"Learn JavaScript in One Day"}"
再看下面的例子🌰:
const stu = { name: "小明", age: 10, score: { math: 88, english: 99 } }; JSON.stringify(stu, ['name', 'score']); // "{"name":"小明","score":{}}"
注意, 最後結果中score是一個空對象, 那麼小明的成績哪兒去了?
好像咱們的目的是要顯示name和score屬性, 只去掉age就好了, 可是score對象中咋啥也沒有了?
那咱們把stu對象從新改改試試:
var stu = { name: "小明", age: 10, score: { math: 88, english: 99, name: 'xiuxiu~', score: 100 } }; JSON.stringify(stu, ['name', 'score']); // "{"name":"小明","score":{"name":"xiuxiu~","score":100}}"
這下看明白了麼, 原來這個過濾是對每一層級的對象都過濾一遍.
若是第二個參數是函數, 行爲會稍有不一樣. 傳入的函數接收兩個參數, 屬性名(鍵)和屬性值(值). 根據屬性名能夠知道應該如何處理要序列化的對象中的屬性. 屬性名只能是字符串, 而在值並不是鍵值對兒結構的值時, 鍵名能夠是空字符串. 函數返回的值就是相應鍵的值, 若是函數返回了undefined
, 那麼相應的屬性會被忽略.
const stu = { name: "小明", age: 10, score: { math: 88, english: 99 } }; const newStu = JSON.stringify(stu, (key, value) => { switch(key) { case 'math': return 100; case 'english': return 100; default: return value; } }); // "{"name":"小明","age":10,"score":{"math":100,"english":100}}" // 成功將小明的成績改爲了100分, 哈哈哈~
2.字符串縮進
JSON.stringify()
方法的第三個參數用於控制結果中的縮進和空白符. 若是這個參數是一個數值, 那它表示的是每一個級別縮進的空格數.
JSON.stringify()
會在結果字符串中插入換行符以提升可讀性.
最大縮進空格數爲10, 全部大於10的值都會自動轉換爲10.
const stu = { name: "小明", age: 10, score: { math: 88, english: 99 } }; JSON.stringify(stu, null, 4); // 4個空格的縮進 /* 序列化後的結果 "{ "name": "小明", "age": 10, "score": { "math": 88, "english": 99 } }" */
若是縮進參數是一個字符串而非數值, 則這個字符串將在JSON字符串中被用做縮進字符(再也不使用空格).
縮進字符串最長不能超過10個字符長. 若是字符串長度超過了10個, 結果中將只出現前10個字符.
JSON.stringify(stu, null, '--'); /* 序列化後的結果 "{ --"name": "小明", --"age": 10, --"score": { ----"math": 88, ----"english": 99 --} }" */
3.toJSON
有時候, JSON.stringify()
仍是不能知足對某些對象進行自定義序列化的需求. 在這些狀況下, 能夠給對象定義toJSON()
方法, 返回其自身的JSON數據格式. 原生Date對象有一個toJSON()
方法,可以將JavaScript的Date對象自動轉換成ISO8601日期字符串(與在Date對象上調用toISOString()
的結果徹底同樣).
JSON.stringify(new Date()); // 序列化後的結果: ""2019-04-08T11:31:05.778Z"" new Date().toJSON(); new Date().toISOString(); // 直接調用toJSON和toISOString方法一樣能獲得字符串: "2019-04-08T11:31:44.432Z"
可讓toJSON()
方法返回任何值, 它都能正常工做.
const stu = { name: "小明", age: 10, score: { math: 88, english: 99 }, toJSON() { return this.name; } }; JSON.stringify(stu); // 序列化後的結果: ""小明""
toJSON()
能夠做爲函數過濾器的補充, 所以理解序列化的內部順序十分重要. 假設把一個對象傳入JSON.stringify()
, 序列化該對象的順序以下:
(1) 若是存在toJSON()
方法並且能經過它取得有效的值, 則調用該方法. 不然, 返回對象自己;
(2) 若是提供了第二個參數, 應用這個函數過濾器. 傳入函數過濾器的值是第(1)步返回的值;
(3) 對第(2)步返回的每一個值進行相應的序列化;
(4) 若是提供了第三個參數, 執行相應的格式化.
JSON.parse()
方法也能夠接收另外一個參數, 該參數是一個函數, 將在每一個鍵值對兒上調用. 爲了區別 JSON.stringify()
接收的替換(過濾)函數(replacer), 這個函數被稱爲還原函數(reviver), 但實際上這兩個函數的簽名是相同的——它們都接收兩個參數, 一個鍵和一個值, 並且都須要返回一個值.
若是還原函數返回undefined
, 則表示要從結果中刪除相應的鍵; 若是返回其餘值, 則將該值插入到結果中.
const stu = { name: "小明", age: 10, score: { math: 88, english: 99 } }; const jsonText = JSON.stringify(stu); JSON.parse(jsonText, (key, value) => { if (key === 'name') { return value + '牛逼了!'; } else { return value; } }); /* 解析結果 { age: 10 name: "小明牛逼了!" score: { math: 88, english: 99 } } */
JSON是一個輕量級的數據格式, 能夠簡化表示複雜數據結構的工做量. JSON使用JavaScript語法的子集表示對象、數組、字符串、數值、布爾值和null
.
ECMAScript5定義了一個原生的JSON對象, 能夠用來將對象序列化爲JSON字符串或者將JSON數據解析爲JavaScript對象. JSON.stringify()
和JSON.parse()
方法分別用來實現上述兩項功能. 這兩個方法都有一些選項, 經過它們能夠改變過濾的方式, 或者改變序列化的過程.