亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

深入理解 JavaScript 中的 JSON

2019-11-19 16:54:44
字體:
來源:轉載
供稿:網友

我們先來看一個JS中常見的JS對象序列化成JSON字符串的問題,請問,以下JS對象通過JSON.stringify后的字符串是怎樣的?先不要急著復制粘貼到控制臺,先自己打開一個代碼編輯器或者紙,寫寫看,寫完再去仔細對比你的控制臺輸出,如果有誤記得看完全文并評論,哈哈。

var friend={   firstName: 'Good',  'lastName': 'Man',  'address': undefined,  'phone': ["1234567",undefined],  'fullName': function(){    return this.firstName + ' ' + this.lastName;  }};JSON.stringify(friend);//這一行返回什么呢? 

第二個問題,如果我想在最終JSON字符串將這個'friend'的姓名全部變成大寫字母,也就是把"Good"變成"GOOD",把"Man"變成"MAN",那么可以怎么做?

基于以上兩個問題,我們再追本溯源問一下,JSON究竟是什么東西?為什么JSON就是易于數據交換?JSON和JS對象的區別?JS中JSON.parse、JSON.stringify和不常見的toJSON,這幾個函數的參數和處理細節到底是怎樣的?

歡迎進入本次“深挖JSON之旅”,下文將從以下幾個方面去理解JSON:

  • 首先是對“JSON是一種輕量的數據交換格式”的理解;
  • 然后來看經常被混為一談的JSON和JS對象的區別;
  • 最后我們再來看JS中這幾個JSON相關函數具體的執行細節。

希望全文能讓如之前的我一樣對JSON一知半解的親能說清楚JSON是什么,也能熟練運用JSON,不看控制臺就知道JS對象序列化成JSON字符串后輸出是啥。

 一、JSON是一種格式,基于文本,優于輕量,用于交換數據 

如果沒有去過JSON的官方介紹可以去一下這里,官方介紹第一、二段已經很清楚地表述了JSON是什么,我將JSON是什么提煉成以下幾個方面:

1. 一種數據格式

什么是格式?就是規范你的數據要怎么表示,舉個栗子,有個人叫“二百六”,身高“160cm”,體重“60kg”,現在你要將這個人的這些信息傳給別人或者別的什么東西,你有很多種選擇:

  • 姓名“二百六”,身高“160cm”,體重“60kg”
  • name="二百六"&height="160cm"&weight="60kg"
  • <person><name>二百六</name><height>160</height><weight>60</weight></person>
  • {"name":"二百六","height":160,"weight":60}
  • ... ...

以上所有選擇,傳遞的數據是一樣的,但是你可以看到形式是可以各式各樣的,這就是各種不同格式化后的數據,JSON是其中一種表示方式。

2. 基于文本的數據格式

JSON是基于文本的數據格式,相對于基于二進制的數據,所以JSON在傳遞的時候是傳遞符合JSON這種格式(至于JSON的格式是什么我們第二部分再說)的字符串,我們常會稱為“JSON字符串”。

3. 輕量級的數據格式

在JSON之前,有一個數據格式叫xml,現在還是廣泛在用,但是JSON更加輕量,如xml需要用到很多標簽,像上面的例子中,你可以明顯看到xml格式的數據中標簽本身占據了很多空間,而JSON比較輕量,即相同數據,以JSON的格式占據的帶寬更小,這在有大量數據請求和傳遞的情況下是有明顯優勢的。

4. 被廣泛地用于數據交換

輕量已經是一個用于數據交換的優勢了,但更重要的JSON是易于閱讀、編寫和機器解析的,即這個JSON對人和機器都是友好的,而且又輕,獨立于語言(因為是基于文本的),所以JSON被廣泛用于數據交換。

以前端JS進行ajax的POST請求為例,后端PHP處理請求為例:

  1. 前端構造一個JS對象,用于包裝要傳遞的數據,然后將JS對象轉化為JSON字符串,再發送請求到后端;
  2. 后端PHP接收到這個JSON字符串,將JSON字符串轉化為PHP對象,然后處理請求。

可以看到,相同的數據在這里有3種不同的表現形式,分別是前端的JS對象、傳輸的JSON字符串、后端的PHP對象,JS對象和PHP對象明顯不是一個東西,但是由于大家用的都是JSON來傳遞數據,大家都能理解這種數據格式,都能把JSON這種數據格式很容易地轉化為自己能理解的數據結構,這就方便啦,在其他各種語言環境中交換數據都是如此。

二、JSON和JS對象之間的“八卦”

很多時候都聽到“JSON是JS的一個子集”這句話,而且這句話我曾經也一直這么認為,每個符合JSON格式的字符串你解析成js都是可以的,直到后來發現了一個奇奇怪怪的東西...

1. 兩個本質不同的東西為什么那么密切

JSON和JS對象本質上完全不是同一個東西,就像“斑馬線”和“斑馬”,“斑馬線”基于“斑馬”身上的條紋來呈現和命名,但是斑馬是活的,斑馬線是非生物。

同樣,"JSON"全名"JavaScript Object Notation",所以它的格式(語法)是基于JS的,但它就是一種格式,而JS對象是一個實例,是存在于內存的一個東西。

說句玩笑話,如果JSON是基于PHP的,可能就叫PON了,形式可能就是這樣的了['propertyOne' => 'foo', 'propertyTwo' => 42,],如果這樣,那么JSON可能現在是和PHP比較密切了。

此外,JSON是可以傳輸的,因為它是文本格式,但是JS對象是沒辦法傳輸的,在語法上,JSON也會更加嚴格,但是JS對象就很松了。

那么兩個不同的東西為什么那么密切,因為JSON畢竟是從JS中演變出來的,語法相近。 

 2. JSON格式別JS對象語法表現上嚴格在哪

 先就以“鍵值對為表現的對象”形式上,對比下兩者的不同,至于JSON還能以怎樣的形式表現,對比完后再羅列。

 

對比內容 JSON JS對象

鍵名

必須是加雙引號

可允許不加、加單引號、加雙引號

屬性值

只能是數值(10進制)、字符串(雙引號)、布爾值和null,
也可以是數組或者符合JSON要求的對象,
不能是函數、NaN, Infinity, -Infinity和undefined

愛啥啥

逗號問題

最后一個屬性后面不能有逗號

可以

數值

前導0不能用,小數點后必須有數字

沒限制

 可以看到,相對于JS對象,JSON的格式更嚴格,所以大部分寫的JS對象是不符合JSON的格式的。

以下代碼引用自這里

var obj1 = {}; // 這只是 JS 對象// 可把這個稱做:JSON 格式的 JavaScript 對象 var obj2 = {"width":100,"height":200,"name":"rose"};// 可把這個稱做:JSON 格式的字符串var str1 = '{"width":100,"height":200,"name":"rose"}';// 這個可叫 JSON 格式的數組,是 JSON 的稍復雜一點的形式var arr = [   {"width":100,"height":200,"name":"rose"},  {"width":100,"height":200,"name":"rose"},  {"width":100,"height":200,"name":"rose"},];// 這個可叫稍復雜一點的 JSON 格式的字符串   var str2='['+   '{"width":100,"height":200,"name":"rose"},'+  '{"width":100,"height":200,"name":"rose"},'+  '{"width":100,"height":200,"name":"rose"},'+']'; 

另外,除了常見的“正常的”JSON格式,要么表現為一個對象形式{...},要么表現為一個數組形式[...],任何單獨的一個10進制數值、雙引號字符串、布爾值和null都是有效符合JSON格式的。

這里有完整的JSON語法參考

3. 一個有意思的地方,JSON不是JS的子集

首先看下面的代碼,你可以copy到控制臺執行下:

var code = '"/u2028/u2029"'; JSON.parse(code); // works fine eval(code); // fails 

這兩個字符/u2028和/u2029分別表示行分隔符和段落分隔符,JSON.parse可以正常解析,但是當做js解析時會報錯。

三、這幾個JS中的JSON函數,弄啥嘞

在JS中我們主要會接觸到兩個和JSON相關的函數,分別用于JSON字符串和JS數據結構之間的轉化,一個叫JSON.stringify,它很聰明,聰明到你寫的不符合JSON格式的JS對象都能幫你處理成符合JSON格式的字符串,所以你得知道它到底干了什么,免得它只是自作聰明,然后讓你Debug long time;另一個叫JSON.parse,用于轉化json字符串到JS數據結構,它很嚴格,你的JSON字符串如果構造地不對,是沒辦法解析的。

而它們的參數不止一個,雖然我們經常用的時候只傳入一個參數。

此外,還有一個toJSON函數,我們較少看到,但是它會影響JSON.stringify。

1. 將JS數據結構轉化為JSON字符串――JSON.stringify

這個函數的函數簽名是這樣的:

JSON.stringify(value[, replacer [, space]]) 

下面將分別展開帶1~3個參數的用法,最后是它在序列化時做的一些“聰明”的事,要特別注意。

1.1 基本使用――僅需一個參數
這個大家都會使用,傳入一個JSON格式的JS對象或者數組,JSON.stringify({"name":"Good Man","age":18})返回一個字符串"{"name":"Good Man","age":18}"

可以看到本身我們傳入的這個JS對象就是符合JSON格式的,用的雙引號,也沒有JSON不接受的屬性值,那么如果像開頭那個例子中的一樣,how to play?不急,我們先舉簡單的例子來說明這個函數的幾個參數的意義,再來說這個問題。

1.2 第二個參數可以是函數,也可以是一個數組

  • 如果第二個參數是一個函數,那么序列化過程中的每個屬性都會被這個函數轉化和處理
  • 如果第二個參數是一個數組,那么只有包含在這個數組中的屬性才會被序列化到最終的JSON字符串中
  • 如果第二個參數是null,那作用上和空著沒啥區別,但是不想設置第二個參數,只是想設置第三個參數的時候,就可以設置第二個參數為null

這第二個參數若是函數

var friend={   "firstName": "Good",  "lastName": "Man",  "phone":"1234567",  "age":18};var friendAfter=JSON.stringify(friend,function(key,value){   if(key==="phone")    return "(000)"+value;  else if(typeof value === "number")    return value + 10;  else    return value; //如果你把這個else分句刪除,那么結果會是undefined});console.log(friendAfter); //輸出:{"firstName":"Good","lastName":"Man","phone":"(000)1234567","age":28}

如果制定了第二個參數是函數,那么這個函數必須對每一項都有返回,這個函數接受兩個參數,一個鍵名,一個是屬性值,函數必須針對每一個原來的屬性值都要有新屬性值的返回。

那么問題來了,如果傳入的不是鍵值對的對象形式,而是方括號的數組形式呢?,比如上面的friend變成這樣:friend=["Jack","Rose"],那么這個逐屬性處理的函數接收到的key和value又是什么?如果是數組形式,那么key是索引,而value是這個數組項,你可以在控制臺在這個函數內部打印出來這個key和value驗證。

這第二個參數若是數組

var friend={   "firstName": "Good",  "lastName": "Man",  "phone":"1234567",  "age":18};//注意下面的數組有一個值并不是上面對象的任何一個屬性名var friendAfter=JSON.stringify(friend,["firstName","address","phone"]);console.log(friendAfter); //{"firstName":"Good","phone":"1234567"}//指定的“address”由于沒有在原來的對象中找到而被忽略

如果第二個參數是一個數組,那么只有在數組中出現的屬性才會被序列化進結果字符串,只要在這個提供的數組中找不到的屬性就不會被包含進去,而這個數組中存在但是源JS對象中不存在的屬性會被忽略,不會報錯。

1.3 第三個參數用于美化輸出――不建議用

指定縮進用的空白字符,可以取以下幾個值:

  • 是1-10的某個數字,代表用幾個空白字符
  • 是字符串的話,就用該字符串代替空格,最多取這個字符串的前10個字符
  • 沒有提供該參數 等于 設置成null 等于 設置一個小于1的數
var friend={   "firstName": "Good",  "lastName": "Man",  "phone":{"home":"1234567","work":"7654321"}};//直接轉化是這樣的://{"firstName":"Good","lastName":"Man","phone":{"home":"1234567","work":"7654321"}}var friendAfter=JSON.stringify(friend,null,4); console.log(friendAfter); /*{  "firstName": "Good",  "lastName": "Man",  "phone": {    "home": "1234567",    "work": "7654321"  }}*/var friendAfter=JSON.stringify(friend,null,"HAHAHAHA"); console.log(friendAfter); /*{HAHAHAHA"firstName": "Good", HAHAHAHA"lastName": "Man", HAHAHAHA"phone": { HAHAHAHAHAHAHAHA"home": "1234567", HAHAHAHAHAHAHAHA"work": "7654321" HAHAHAHA} }*/var friendAfter=JSON.stringify(friend,null,"WhatAreYouDoingNow"); console.log(friendAfter); /* 最多只取10個字符{WhatAreYou"firstName": "Good", WhatAreYou"lastName": "Man", WhatAreYou"phone": { WhatAreYouWhatAreYou"home": "1234567", WhatAreYouWhatAreYou"work": "7654321" WhatAreYou} }*/

笑笑就好,別這樣用,序列化是為了傳輸,傳輸就是能越小越好,加莫名其妙的縮進符,解析困難(如果是字符串的話),也弱化了輕量化這個特點。

1.4 注意這個函數的“小聰明”(重要)

如果有其他不確定的情況,那么最好的辦法就是"Have a try",控制臺做下實驗就明了。

  • 鍵名不是雙引號的(包括沒有引號或者是單引號),會自動變成雙引號;字符串是單引號的,會自動變成雙引號

  • 最后一個屬性后面有逗號的,會被自動去掉

  • 非數組對象的屬性不能保證以特定的順序出現在序列化后的字符串中 
    這個好理解,也就是對非數組對象在最終字符串中不保證屬性順序和原來一致

  • 布爾值、數字、字符串的包裝對象在序列化過程中會自動轉換成對應的原始值 
    也就是你的什么new String("bala")會變成"bala",new Number(2017)會變成2017

  • undefined、任意的函數(其實有個函數會發生神奇的事,后面會說)以及 symbol 值(symbol詳見ES6對symbol的介紹)

    • 出現在非數組對象的屬性值中:在序列化過程中會被忽略
    • 出現在數組中時:被轉換成 null
JSON.stringify({x: undefined, y: function(){return 1;}, z: Symbol("")}); //出現在非數組對象的屬性值中被忽略:"{}"JSON.stringify([undefined, Object, Symbol("")]); //出現在數組對象的屬性值中,變成null:"[null,null,null]"

NaN、Infinity和-Infinity,不論在數組還是非數組的對象中,都被轉化為null

所有以 symbol 為屬性鍵的屬性都會被完全忽略掉,即便 replacer 參數中強制指定包含了它們

不可枚舉的屬性會被忽略

2. 將JSON字符串解析為JS數據結構――JSON.parse

這個函數的函數簽名是這樣的:

JSON.parse(text[, reviver]) 

如果第一個參數,即JSON字符串不是合法的字符串的話,那么這個函數會拋出錯誤,所以如果你在寫一個后端返回JSON字符串的腳本,最好調用語言本身的JSON字符串相關序列化函數,而如果是自己去拼接實現的序列化字符串,那么就尤其要注意序列化后的字符串是否是合法的,合法指這個JSON字符串完全符合JSON要求的嚴格格式

值得注意的是這里有一個可選的第二個參數,這個參數必須是一個函數,這個函數作用在屬性已經被解析但是還沒返回前,將屬性處理后再返回。

var friend={   "firstName": "Good",  "lastName": "Man",  "phone":{"home":"1234567","work":["7654321","999000"]}};//我們先將其序列化var friendAfter=JSON.stringify(friend); //'{"firstName":"Good","lastName":"Man","phone":{"home":"1234567","work":["7654321","999000"]}}'//再將其解析出來,在第二個參數的函數中打印出key和valueJSON.parse(friendAfter,function(k,v){   console.log(k);  console.log(v);  console.log("----");});/*firstName Good ----lastName Man ----home 1234567 ----0 7654321 ----1 999000 ----work []----phone Object ----Object ----*/

仔細看一下這些輸出,可以發現這個遍歷是由內而外的,可能由內而外這個詞大家會誤解,最里層是內部數組里的兩個值啊,但是輸出是從第一個屬性開始的,怎么就是由內而外的呢?

這個由內而外指的是對于復合屬性來說的,通俗地講,遍歷的時候,從頭到尾進行遍歷,如果是簡單屬性值(數值、字符串、布爾值和null),那么直接遍歷完成,如果是遇到屬性值是對象或者數組形式的,那么暫停,先遍歷這個子JSON,而遍歷的原則也是一樣的,等這個復合屬性遍歷完成,那么再完成對這個屬性的遍歷返回。

本質上,這就是一個深度優先的遍歷。

有兩點需要注意:

  • 如果 reviver 返回 undefined,則當前屬性會從所屬對象中刪除,如果返回了其他值,則返回的值會成為當前屬性新的屬性值。
  • 你可以注意到上面例子最后一組輸出看上去沒有key,其實這個key是一個空字符串,而最后的object是最后解析完成對象,因為到了最上層,已經沒有真正的屬性了。

3. 影響 JSON.stringify 的神奇函數――object.toJSON

如果你在一個JS對象上實現了toJSON方法,那么調用JSON.stringify去序列化這個JS對象時,JSON.stringify會把這個對象的toJSON方法返回的值作為參數去進行序列化。

var info={   "msg":"I Love You",  "toJSON":function(){    var replaceMsg=new Object();    replaceMsg["msg"]="Go Die";    return replaceMsg;  }};JSON.stringify(info); //出si了,返回的是:'"{"msg":"Go Die"}"',說好的忽略函數呢

這個函數就是這樣子的。

其實Date類型可以直接傳給JSON.stringify做參數,其中的道理就是,Date類型內置了toJSON方法。

四、小結以及關于兼容性的問題

到這里終于把,JSON和JS中的JSON,梳理了一遍,也對里面的細節和注意點進行了一次遍歷,知道JSON是一種語法上衍生于JS語言的一種輕量級的數據交換格式,也明白了JSON相對于一般的JS數據結構(尤其是對象)的差別,更進一步,仔細地討論了JS中關于JSON處理的3個函數和細節。

不過遺憾的是,以上所用的3個函數,不兼容IE7以及IE7之前的瀏覽器。有關兼容性的討論,留待之后吧。如果想直接在應用上解決兼容性,那么可以套用JSON官方的js,可以解決。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
精品视频在线导航| 久久色精品视频| 大伊人狠狠躁夜夜躁av一区| 亚洲精品国产拍免费91在线| 久久九九精品99国产精品| 国产精品日本精品| 国产在线98福利播放视频| 国产美女精彩久久| 成人午夜激情免费视频| 国产中文字幕91| 性亚洲最疯狂xxxx高清| 国产精品久久久久久久久久久久久| 欧美高清在线视频观看不卡| 亚洲电影免费观看高清完整版在线观看| 久久资源免费视频| 成人国产在线视频| 国产精品视频永久免费播放| 亚洲欧美日韩久久久久久| 国产欧美精品久久久| 久久久久久久久久久久久久久久久久av| 欧美激情在线播放| 日韩成人网免费视频| 日韩极品精品视频免费观看| 日本久久久久久久久久久| 久久久精品视频成人| 国产精品毛片a∨一区二区三区|国| 欧美寡妇偷汉性猛交| 国产精品高潮呻吟久久av野狼| 亚洲欧美精品一区二区| 国产精品视频内| 亚洲成年人在线| 欧美尤物巨大精品爽| 亚洲激情自拍图| 国产日韩欧美视频在线| 精品中文字幕在线2019| 国产在线播放91| 91亚洲精品一区| 亚洲欧美日韩图片| 亚洲高清av在线| 在线观看国产欧美| 中文字幕亚洲第一| 91视频88av| 国产精品海角社区在线观看| 国内精品久久久久影院 日本资源| 亚洲91av视频| 亚洲一区亚洲二区亚洲三区| www日韩欧美| 成人精品一区二区三区| 国产成人91久久精品| 久久久久亚洲精品| 亚洲欧美中文日韩在线| 久久视频在线直播| 成人国产精品一区二区| 成人免费福利视频| 中文欧美日本在线资源| 成人两性免费视频| 97成人在线视频| 欧美最顶级丰满的aⅴ艳星| 欧美中文字幕在线| 亚洲天堂2020| 国产噜噜噜噜噜久久久久久久久| www.日韩不卡电影av| 亚洲精品国产精品国自产观看浪潮| 国产欧美一区二区三区久久人妖| 欧美激情成人在线视频| 国产香蕉97碰碰久久人人| 日韩av电影在线免费播放| 色综合视频网站| 日韩经典中文字幕| 久久免费视频观看| 久久久av亚洲男天堂| 97碰在线观看| 国产精品成人一区| 国产精品日韩欧美大师| 久久精品视频在线| 久久国产精品久久精品| 97碰在线观看| 国内精品美女av在线播放| 国内精品模特av私拍在线观看| 国产在线精品成人一区二区三区| 欧美国产第二页| 欧美日韩综合视频| 热久久免费国产视频| 中文欧美日本在线资源| 国产成人综合亚洲| 成人性生交大片免费观看嘿嘿视频| 国产精品91一区| 91精品久久久久久久久久久久久久| 成人淫片在线看| 中文字幕欧美精品日韩中文字幕| 亚洲男人的天堂网站| 日韩亚洲欧美中文高清在线| 成人精品视频在线| 2019日本中文字幕| 国产一区二区三区在线观看视频| 亚洲成人激情在线| 欧美高清不卡在线| 久久久久久久影院| 成人美女免费网站视频| 日韩av片电影专区| 亚洲精品国偷自产在线99热| 国产成人精品在线观看| 国产成人一区二区三区电影| 欧美色道久久88综合亚洲精品| 都市激情亚洲色图| 国产精品视频播放| 日本高清视频精品| 92福利视频午夜1000合集在线观看| 97超级碰碰碰久久久| 亚洲女人天堂网| 日韩欧美精品网址| 欧美高清视频一区二区| 亚洲女人天堂av| 欧美精品电影免费在线观看| 伊人久久精品视频| 欧美成人激情视频免费观看| 国产国语videosex另类| 亚洲欧美日韩综合| 国产区精品视频| 国产精品人人做人人爽| 一区二区三区回区在观看免费视频| 亚洲自拍偷拍色图| 成人免费视频网| 日韩精品视频三区| 最近2019中文字幕第三页视频| 久久青草精品视频免费观看| 国产精品青青在线观看爽香蕉| 国产精品福利久久久| 亚洲图片制服诱惑| 一级做a爰片久久毛片美女图片| 欧美国产在线电影| 日本精品久久久久久久| 亚洲精品福利在线观看| 亚洲欧美一区二区精品久久久| 国产福利视频一区二区| 日韩免费观看视频| 亚洲网站在线观看| 色偷偷偷亚洲综合网另类| 国产精品伦子伦免费视频| 亚洲激情免费观看| 日韩精品免费在线观看| 黄色一区二区在线| 日韩欧美国产骚| 中文日韩在线观看| 欧美极品少妇xxxxⅹ喷水| 久久99国产精品久久久久久久久| 亚洲成**性毛茸茸| 国产精品人成电影在线观看| 中文字幕日韩av电影| 欧美亚洲另类制服自拍| 欧美激情一区二区三级高清视频| 日韩在线视频线视频免费网站| 乱亲女秽乱长久久久| 欧美日韩成人在线播放| 国产亚洲一区精品| 蜜臀久久99精品久久久无需会员| 国产精品99久久久久久久久久久久| 亚洲一区二区福利| 97视频免费在线看| 欧美精品久久久久a| 亚洲视频电影图片偷拍一区| 欧美性猛交xxxx富婆弯腰| 在线播放精品一区二区三区| 欧美性猛交xxxxx免费看|