JSON作為一種輕量的數據傳輸格式,越來越受到人們的青睐。下面是我仿照Prototype的一些實現。
JSONFilter:/^///*-secure-([/s/S]*)/*///s*$/,
unfilterJSON:
function
(json,filter) {
return
json.replace((filter || dom.JSONFilter),
function
(a,b){
return
b ||
""
});
},
JSONFilter完全抄自Prototype,因為後台基本上只會傳輸兩種格式的東西給我們——文本(xmlhttp.responseText)與XML(xmlhttp.responseXML)。如果要json,我們可以eval一下,或者使用現代浏覽器提供的JSON.parse方法。但問題就出在eval中,很容易出現XSS攻擊。如果文本是注釋就可以避免這問題,在Prototype中還檢察一下請求的來源。對於自家的網站的請求,我們完全可以在請求前處理一下,讓它變成如下格式:
var
text =
'/*-secure-/n{"name": "Violet", "occupation": "character", "age": 25}/n*/'
dom.unfilterJSON(text)
// -> '{"name": "Violet", "occupation": "character", "age": 25}'
到時我們用unfilterJSON函數提取合法的字段來eval就沒問題了。
第二個函數,判斷字符串是否符合JSON的格式。JSON是有固定的格式,要求鍵必須被雙引號括起來。下面的函數提取自json2:
isJSONText:
function
(json){
//
return
/^[/],:{}/s]*$/.test(json.replace(///(?:[
"////bfnrt]|u[0-9a-fA-F]{4})/g, "
@")
.replace(/
"[^"
///n/r]*
"|true|false|null|-?/d+(?:/./d*)?(?:[eE][+/-]?/d+)?/g, "
]")
.replace(/(?:^|:|,)(?:/s*/[)+/g,
""
));
},
第三個函數,將符合JSON的格式的文本變成JSON對象。第二參數用於指明此文本是否安全(如,是否同源請求)。如果能用原生對象的parse方法就用原生的,否則動態解析它。之所以不用eval,是因為ecma那幫人頭腦發熱,想禁掉它。
evalJSON:
function
( json ,sanitize) {
if
( !is(json,
"String"
) || !json )
return
null
;
json = dom.unfilterJSON(json);
//判定是否符合JSON的格式 from http://json.org/json2.js
if
( !sanitize || dom.isJSONText(json) ) {
return
window.JSON && window.JSON.parse ?
window.JSON.parse( json ) : (
new
Function(
"return "
+ json))();
}
else
{
throw
"Invalid JSON: "
+ json;
}
}
//http://www.21kaiyun.com
var
data =dom.evalJSON(
'{ "name": "Violet", "occupation": "character" }'
);
data.name;
//-> "Violet"
第四函數,將JSON對象變成文本。
// http://www.21kaiyun.com
//21世紀開運網
// var a = [1,2,3,4,{aa:8}]; puts(dom.toJSON(a))
toJSON:
function
(obj){
if
(window.JSON && window.JSON.stringify) {
return
JSON.stringify(obj)
}
if
(
typeof
window.uneval ==
'function'
) {
return
uneval(obj);
}
if
(
typeof
obj ==
'object'
) {
if
(!obj) {
return
'null'
;
}
var
list = [];
if
(dom.is(obj,
"Array"
)) {
for
(
var
i=0,n=obj.length;i <n;i++) {
list.push(dom.toJSON(obj[i]));
}
return
'['
+ list.join(
','
) +
']'
;
}
else
{
for
(
var
prop
in
obj) {
if
(obj[has](prop))
list.push(
'"'
+ prop +
'":'
+ dom.toJSON(obj[prop]));
}
return
'{'
+ list.join(
','
) +
'}'
;
}
}
else
if
(
typeof
obj ==
'string'
) {
return
'"'
+ obj.replace(/(["
'])/g, '
//$1
') + '
"';
}
else
{
return
new
String(obj);
}
},
toJSON :
function
(obj) {
if
(is(window.JSON,
"JSON"
)){
return
JSON.stringify(obj)
}
function
f(n) {
return
n < 10 ?
'0'
+ n : n;
}
var
escapable = /[///"/x00-/x1f/x7f-/x9f/u00ad/u0600-/u0604/u070f/u17b4/u17b5/u200c-/u200f/u2028-/u202f/u2060-/u206f/ufeff/ufff0-/uffff]/g;
var
meta = {
'/b'
:
'//b'
,
'/t'
:
'//t'
,
'/n'
:
'//n'
,
'/f'
:
'//f'
,
'/r'
:
'//r'
,
'"'
:
'//"'
,
'//'
:
'////'
};
function
_char(c) {
return
meta[c] ? meta[c] :
'//u'
+(
'0000'
+(+(c.charCodeAt(0))).toString(16)).slice(-4);
}
function
_string(s) {
return
'"'
+ s.replace(escapable, _char) +
'"'
;
}
if
(is(obj,
"Date"
)){
return
'"'
+ obj.getUTCFullYear() +
'-'
+
f(obj.getUTCMonth() + 1) +
'-'
+
f(obj.getUTCDate()) +
'T'
+
f(obj.getUTCHours()) +
':'
+
f(obj.getUTCMinutes()) +
':'
+
f(obj.getUTCSeconds()) +
'Z"'
;
}
else
if
(is(obj,
"Number"
)){
return
isFinite(obj) ? obj+
''
:
'null'
;
}
else
if
(is(obj,
"Boolean"
)){
return
obj+
''
}
else
if
(is(obj,
"String"
)){
return
_string(obj)
}
else
if
(obj ===
null
){
return
"null"
}
else
if
(is(obj,
"Array"
)){
return
'['
+ (dom.filter(obj,
function
(value){
return
dom.toJSON(value) !== undefined;
})).join(
', '
) +
']'
;
}
else
if
(is(obj,
"Object"
)){
var
results = [];
dom.each(obj,
function
(value,key){
value = dom.toJSON(value)
if
(!value)
results.push(dom.toJSON(key) +
': '
+ value);
});
return
'{'
+ results.join(
', '
) +
'}'
;
}
else
{
return
undefined;
}
},
Is函數
目前最精確的判定方法(不包括自定義類型)
//2010.6.1更新
var
is =
function
(obj,type) {
return
(type ===
"Null"
&& obj ===
null
) ||
(type ===
"Undefined"
&& obj === void 0 ) ||
(type ===
"Number"
&& isFinite(obj)) ||
Object.prototype.toString.call(obj).slice(8,-1) === type;
},
用法如下:
//***************示例一,判定數組與函數
var
forEach =
function
(array,fn,bind){
if
(is(array,
"Array"
) && is(Array.forEach,
"Function"
)){
array.forEach(fn,bind);
}
else
{
for
(
var
i=0,n=array.length;i<n;i++){
i
in
array && fn.call(bind,array[i],i,array)
}
}
}
//***************示例二,判定null
var
a =
null
alert(is(a,
"Null"
))
//***************示例二,判定undefined
var
b
alert(is(b,
"Undefined"
))
另一個變種,直接返回表示類型的字符串
var
getType =
function
(obj) {
var
_toString = Object.prototype.toString,undefined;
return
obj ===
null
?
"Null"
:
obj === undefined ?
"Undefined"
:
_toString.call(obj).slice(8,-1);
};
用法:
var
arr = [1,2,3,4]
alert(getType(arr));
//Array
var
nil =
null
alert(getType(nil))
//Null
var
und ;
alert(getType(und))
//Undefined
var
spans = document.getElementsByTagName(
"span"
);
alert(getType(spans))
//HTMLCollection
alert(getType(spans[0].childNodes))
//NodeList
//2010.7.20
function
isA (thing, canon) {
// special case for null and undefined
if
(thing ==
null
|| canon ==
null
) {
return
thing === canon;
}
return
Object.getPrototypeOf(Object(thing)) == Object.getPrototypeOf(Object(canon));
}
function
isBool (thing) {
return
isA(thing,
true
);
}
function
isNumber (thing) {
return
isA(thing, 0) && isFinite(thing);
}
function
isString (thing) {
return
isA(thing,
""
);
}