這種新的原生JSON功能能夠使Internet Explorer 8對現有的AJAX應用程序運行得更加快速和安全。
什麼是JSON?大多數開發者不是只進行AJAX程序程序開發的,我這裡先介紹一點背景知識。JSON是一種簡單的、人能夠閱讀的數據交換格式,在AJAX程序中,當服務器與web程序之間傳輸數據時,通常采用這種格式。
舉例來說,假如你從收藏的web郵件中選擇一個聯系人名稱,以便能夠看到該聯系人信息。服務器向web程序(運行在浏覽器中)發送的數據流可能是下面的樣子:
{
"firstName": "cyra",
"lastName": "richardson",
"address": {
"streetAddress": "1 Microsoft way",
"city": "Redmond",
"state": "WA",
"postalCode": 98052
},
"phoneNumbers": [
"425-777-7777",
"206-777-7777"
]
}
值得慶幸的是,這種格式與JavaScript的語法完全兼容。當今的很多程序使用Javascript的eval()函數將這種得到的數據轉換成 Javascript對象。使用eval()是不安全的,並且耗費資源。eval()將這個字符串解析為Jscript表達式,並且執行。如果傳遞給 eval()的字符串被篡改過,它就可能含有我們不期望的數據,甚至是別人的代碼,這樣就注入到了你的web程序中。
現在,有很多采用 Javascript編寫的庫,用來更加安全地解析不受信任的JSON數據。有些使用Jscript編寫的解析器(http: //www.json.org/json_parser.js)對數據進行了嚴格的驗證,有些庫,像json2,js(http: //www.json.org/json2.js),采用正則表達式對輸入的字符串進行全面的檢查,然後使用eval()快速解析。理想的解決方案是一種原生實現方法,避免應用程序遭受代碼注入,運行很快,並且隨處都能使用。
IE8 Jscript中原生JSONIE8 的Jscript引擎已經有了JSON完全的原生實現,在保持與ES3.1提案草案(Proposal Working Draft,地址http://wiki.ecmascript.org/doku.php?id=es3.1: es3.1_proposal_working_draft)中所描述的JSON支持的兼容性的同時,極大地提高了序列化、反序列化的速度,並且提高解析不信任數據的安全性。
API我們定義了一個新的內置對象“JSON”,這個對象可被修改或者重寫。看上去很像math或者其他內置的全局對象。除了JSON對象之外,toJSON()這些特定的函數也添加到了Date、Number、String和 boolean對象的原型上。JSON對象有兩個方法:parse()和stringify()。
例如:
var jsObjString = "{\"memberNull\" : null, \"memberNum\" : 3, \"memberStr\" : \"StringJSON\", \"memberBool\" : true , \"memberObj\" : { \"mnum\" : 1, \"mbool\" : false}, \"memberX\" : {}, \"memberArray\" : [33, \"StringTst\",null,{}]";
var jsObjStringParsed = JSON.parse(jsObjString);
var jsObjStringBack = JSON.stringify(jsObjStringParsed);
這個由parse()方法產生、又通過stringify()方法序列化回去的對象與下面的對象是完全一樣的:
var jsObjStringParsed =
{
"memberNull" : null,
"memberNum" : 3,
"memberStr" : "StringJSON",
"memberBool" : true ,
"memberObj" :
{
"mnum" : 1,
"mbool" : false
},
"memberX" : {},
"memberArray" :
[
33,
"StringTst",
null,
{}
]
};
JSON.parse(source, reviver)JSON.parse方法執行反序列化,它采用JSON格式的字符串(由參數source指定),產生Jscript對象或者數組。
可選參數revive是一個用戶自定義函數,用來計入解析的變化。結果對象或者數組遞歸遍歷,reviver函數用在每一個成員上,每個成員值被 reviver的返回值所替代。如果reviver返回null,則對象成員被刪除。對reviver的遍歷和調用是按後序遍歷完成的。也就是說:對象的所有成員被“revived”之後,整個對象也就“revived”了。
reviver主要用來識別類似ISO這樣的字符串,將它們轉成 Date對象。到目前為止,JSON格式(http://www.json.org/)對Date對象來說,是不能來回轉換的,這是因為沒有 Jscript的標准Date文字量。ES3.1草案(http://wiki.ecmascript.org/doku.php?id=es3.1: es3.1_proposal_working_draft)包含了一個如何使用reviver函數解決這個問題的例子。
JSON.stringify(value, replacer, space)這個是序列化方法。它以由value參數指定的對象或者數組為參數,生成JSON格式的字符串。對象或者數組遞歸訪問,序列化成特定的JSON格式。如果 value參數有toJSON()方法,那麼這個方法就起第一個過濾器的作用,原始的value被value.toJSON(key)替代,最終的值被序列化。參數key是一個字符串,當類似(key:value)這樣的對象被序列化時,key是成員的名字。對根對象來說,key是空字符串。
Date.prototype.toJSON()生成一個無需轉義的字符串,是真正的序列化器,因為stringify()會返回最原始、沒有任何變化的字符串。Date對象通過toJSON()方法進行序列化。
Number.prototype.toJSON ()、String.prototype.toJSON()、 Boolean.prototype.toJSON()函數返回ValueOf()。他們用來進行對象的正確序列化,像“ var num = new Number(3.14);”這樣的對象。
可選的replacer參數起過濾器的作用,遞歸使用。它可以是個函數,也可以是個數組。如果 replacer是一個函數,那麼對每個對象成員key:value都調用replacer(key,value)。至於根對象,調用replacer ("",value)。如果replacer是個數組,則必須是個數組字符串。數組的元素就是要進行序列化成員的名字。序列化的順序按照數組中的名字順序。在序列化數組時,數組replacer是被忽略的。
可選的參數space是關於如何格式化輸出文字的,如果該參數省略,則輸出文字沒有任何額外的空格。如果它是一個數字,它指定的是每個級別縮進的空格數。如果它是一個字符(比如"\t"或者“ ”),它就以這些字符縮進每一個級別的字符。
對現有的網頁有何影響?ES3.1 JSON提案是被流行的json2.js所使用的主要因素。我們也采用JSON這個名字。全局對象JSON能夠被重寫。然而,它不再是一個未定義的對象。這與通過在腳本語言中引入new關鍵字是相同的。采用一個名字偶爾會影響現有的代碼。使用json2.js的頁面不太可能會受影響。除了極少數的例外,所有這些頁面都將會繼續正常工作,只能是運行得更快。
那些自己實現的JSON對象定義的頁面可能會受到影響,尤其是使用類似“if(!this.JSON) { JSON=…}”這種模式定義的JSON對象。有兩種主要的方法可以解決這個問題:
1,將現有代碼遷移,使用原生JSON對象
如果自己的JSON實現是基於json2.js的某種版本的,遷移起來就很簡單。
2,決定不使用原生JSON支持,繼續使用自己現有的JSON對象
這可以通過重命名或者重寫JSON名字實現。重命名意味著要將所有使用JSON名字的代碼修改成類似“MyJSON”這樣的名字。重寫意味著確保自己的 JSON定義重寫所有使用默認原生JSON定義的代碼。大多數情況下,只需移除條件“if(!this.JSON)”就可以了。
考慮到3.1標准的影響,使用JSON這個名字與我們通過定義好的接口進行互操作的願望是一致的。
關於原生JSON,要談論的事情還有很多。解析器不是基於eval() 的,是一個獨立的實現。它與JSON支持(http://wiki.ecmascript.org/doku.php?id=es3.1: json_support)提供的引用解析器是等同的。它也是和http://www.json.org/json_parser.js一樣安全的,並且運行速度要快很多。所以,如果你使用eval(),或自己的JSON庫,請檢查一下IE8中原生JSON實現,以便得到更好的性能和更安全的操作。