JSON對象 支持到IE8,舊版的IE需要Polyfill
本文參考MDN做的詳細整理,方便大家參考[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript)
JSON對象本身不能被調用或者作為構造函數,除了它的這兩個方法屬性外 JSON 對象本身並沒有什么有用的功能。
每個JSON對象,就是一個值。要么是簡單類型的值,要么是復合類型的值,但是只能是一個值,不能是兩個或更多的值。這就是說,每個JSON文檔只能包含一個值。
描述
JSON 是一種用來序列化對象、數組、數值、字符串、布爾值和 null 的語法。它基於 JavaScript 語法,但是又有區別:一些 JavaScript 值不是 JSON,而某些 JSON 不是 JavaScript 值。參考 JSON: The JavaScript subset that isn't。
JavaScript 與 JSON 的區別
JSON對值的類型和格式有嚴格的規定。
- 復合類型的值只能是數組或對象,不能是函數、正則表達式對象、日期對象。
- 簡單類型的值只有四種:字符串、數值(必須以十進制表示)、布爾值和null(不能使用NaN, Infinity, -Infinity和undefined)。
- 字符串必須使用雙引號表示,不能使用單引號。
- 對象的鍵名必須放在雙引號里面。
- 數組或對象最后一個成員的后面,不能加逗號。
JavaScript類型 |
JSON與之區別 |
對象和數組 |
屬性名稱必須用雙引號包裹;最后一個屬性后面不能有逗號。 |
數值 |
前導0不能使用(在 JSON.stringify 中將會被忽略,在 JSON.parse 會拋出錯誤);小數點后面至少有一個數字。 |
字符串 |
只有有限的字符能夠被轉義;不允許某些控制字符;但允許使用Unicode 行分隔符 (U+2028) 和段落分隔符 (U+2029) ; 字符串必須用雙引號括起來。 在 Javascript 中,下面的示例中 JSON.parse() 能夠正常解析但 SyntaxError 會出錯: var code = '"\u2028\u2029"'; JSON.parse(code); // works fine eval(code); // fails
|
完整的JSON語法如下:
JSON = null
or true or false
or JSONNumber
or JSONString
or JSONObject
or JSONArray
JSONNumber = - PositiveNumber
or PositiveNumber
PositiveNumber = DecimalNumber
or DecimalNumber . Digits
or DecimalNumber . Digits ExponentPart
or DecimalNumber ExponentPart
DecimalNumber = 0
or OneToNine Digits
ExponentPart = e Exponent
or E Exponent
Exponent = Digits
or + Digits
or - Digits
Digits = Digit
or Digits Digit
Digit = 0 through 9
OneToNine = 1 through 9
JSONString = ""
or " StringCharacters "
StringCharacters = StringCharacter
or StringCharacters StringCharacter
StringCharacter = any character
except " or \\ or U+0000 through U+001F
or EscapeSequence
EscapeSequence = \\" or \\/ or \\\\ or \\b or \\f or \\n or \\r or \\t
or \\u HexDigit HexDigit HexDigit HexDigit
HexDigit = 0 through 9
or A through F
or a through f
JSONObject = { }
or { Members }
Members = JSONString : JSON
or Members , JSONString : JSON
JSONArray = [ ]
or [ ArrayElements ]
ArrayElements = JSON
or ArrayElements , JSON
空白字符可以出現在任意位置,但是數值類型的數字中間不能有空白字符,字符串中間不能隨意添加空白字符,因為添加的空白字符會被解釋為相應的字符值,從而引起錯誤。有效空白字符只包括制表符 (U+0009)、回車符 (U+000D)、換行符 (U+000A) 和空格 (U+0020) 。
方法
JSON.parse(text[, reviver])
解析JSON字符串text, 可以選擇改變前面解析后的值及其屬性,然后返回解析的值。它從 text 字符串解析出的一個 Object。如果被解析的 JSON 字符串包含語法錯誤,則會拋出 SyntaxError 異常。
- text 要解析的 JSON 字符串,可以查看 JSON 一文了解 JSON 的語法。
- reviver 可選 一個函數,用來轉換解析出的屬性值
- 使用 reviver 函數:
如果指定了 reviver 函數,則解析出的 JavaScript 值(解析值)會經過一次轉換后才將被最終返回(返回值)。更具體點講就是:解析值本身以及它所包含的所有屬性,會按照一定的順序(從最最里層的屬性開始,一級級往外,最終到達頂層,也就是解析值本身)分別的去調用 reviver 函數,在調用過程中,當前屬性所屬的對象會作為 this 值,當前屬性名和屬性值會分別作為第一個和第二個參數傳入 reviver 中。如果 reviver 返回 undefined,則當前屬性會從所屬對象中刪除,如果返回了其他值,則返回的值會成為當前屬性新的屬性值。
當遍歷到最頂層的值(解析值)時,傳入 reviver 函數的參數會是空字符串 ""(因為此時已經沒有真正的屬性)和當前的解析值(有可能已經被修改過了),當前的 this 值會是 {"": 修改過的解析值},在編寫 reviver 函數時,要注意到這個特例。(譯者按:這個函數的遍歷順序按深度優先遍歷)
JSON.parse('{"p": 5}', function (key, value) {
if(key === '') return value; // 如果到了最頂層,則直接返回屬性值,
return value* 2; // 否則將屬性值變為原來的 2 倍。
}); // { p: 10 }
JSON.parse('{"1": 1, "2": 2,"3": {"4": 4, "5": {"6": 6}}}', function (k, v) {
console.log(k); // 輸出當前的屬性名,從而得知遍歷順序是從內向外的,
// 最后一個屬性名會是個空字符串。
return v; // 返回原始屬性值,相當於沒有傳遞 reviver 參數。
});
// 1
// 2
// 4
// 6
// 5
// 3// ""
JSON.stringify(value[, replacer [, space]])
將任意的 JavaScript 值序列化成 JSON 字符串。若轉換的函數被指定,則被序列化的值的每個屬性都會經過該函數的轉換和處理;若轉換的數組被指定,只有包含在這個數組中的屬性名才會被序列化到最終的 JSON 字符串中。
- value 將要序列化成 JSON 字符串的值。
- replacer 可選 如果該參數是一個函數,則在序列化過程中,被序列化的值的每個屬性都會經過該函數的轉換和處理;
如果該參數是一個數組,則只有包含在這個數組中的屬性名才會被序列化到最終的 JSON 字符串中;
如果該參數為null或者未提供,則對象所有的屬性都會被序列化;
關於該參數更詳細的解釋和示例,請參考使用原生的 JSON 對象一文。
- space 可選 指定縮進用的空白字符串,用於美化輸出(pretty-print);
如果參數是個數字,它代表有多少的空格;上限為10。改值若小於1,則意味着沒有空格;
如果該參數為字符串(字符串的前十個字母),該字符串將被作為空格;
如果該參數沒有提供(或者為null)將沒有空格。
關於序列化,有下面五點注意事項:
- 非數組對象的屬性不能保證以特定的順序出現在序列化后的字符串中。
- 布爾值、數字、字符串的包裝對象在序列化過程中會自動轉換成對應的原始值。
- undefined、任意的函數以及 symbol 值,在序列化過程中會被忽略(出現在非數組對象的屬性值中時)或者被轉換成 null(出現在數組中時)。
- 所有以 symbol 為屬性鍵的屬性都會被完全忽略掉,即便 replacer 參數中強制指定包含了它們。
- 不可枚舉的屬性會被忽略
JSON.stringify({}); // '{}'
JSON.stringify(true); // 'true'
JSON.stringify("foo"); // '"foo"'
JSON.stringify([1, "false", false]); // '[1,"false",false]'
JSON.stringify({ x: 5 }); // '{"x":5}'
JSON.stringify({x: 5, y: 6});
// '{"x":5,"y":6}' 或者 '{"y":6,"x":5}' 都可能
JSON.stringify([new Number(1), new String("false"), new Boolean(false)]);
// '[1,"false",false]'
JSON.stringify({x: undefined, y: Object, z: Symbol("")});
// '{}'
JSON.stringify([undefined, Object, Symbol("")]);
// '[null,null,null]'
JSON.stringify({[Symbol("foo")]: "foo"});
// '{}'
JSON.stringify({[Symbol.for("foo")]: "foo"}, [Symbol.for("foo")]);
// '{}'
JSON.stringify({[Symbol.for("foo")]: "foo"}, function (k, v) {
if (typeof k === "symbol"){
return "a symbol";
}
});
// '{}'
// 不可枚舉的屬性默認會被忽略:
JSON.stringify( Object.create(null, { x: { value: 'x', enumerable: false }, y: { value: 'y', enumerable: true } }) );
// '{"y":"y"}'
replacer參數
replacer參數可以是一個函數或者一個數組。作為函數,它有兩個參數,鍵(key)值(value)都會被序列化。
- 如果返回一個 Number, 轉換成相應的字符串被添加入JSON字符串。
- 如果返回一個 String, 該字符串作為屬性值被添加入JSON。
- 如果返回一個 Boolean, "true" 或者 "false"被作為屬性值被添加入JSON字符串。
- 如果返回任何其他對象,該對象遞歸地序列化成JSON字符串,對每個屬性調用replaceer方法。除非該對象是一個函數,這種情況將不會被序列化成JSON字符串。
- 如果返回undefined,該屬性值不會被宰JSON字符串中輸出。
注意: 不能用replacer方法,從數組中移除值(values),如若返回undefined或者一個函數,將會被null取代。
例子1(function)
function replacer(key, value) {
if (typeof value === "string") {
return undefined;
}
return value;
}
var foo = {foundation: "Mozilla", model: "box", week: 45, transport: "car", month: 7};
var jsonString = JSON.stringify(foo, replacer);
JSON序列化結果為 {"week":45,"month":7}.
例子2(array)
如果replacer是一個數組,數組的值代表將被序列化成JSON字符串的屬性名。
JSON.stringify(foo, ['week', 'month']);
// '{"week":45,"month":7}', 只保留“week”和“month”屬性值。
space 參數
space 參數用來控制結果字符串里面的間距。如果是一個數字, 則在字符串化時每一級別會比上一級別縮進多這個數字值的空格(最多10個空格);如果是一個字符串,則每一級別會比上一級別多縮進用該字符串(或該字符串的前十個字符)。
JSON.stringify({ a: 2 }, null, " "); // '{\n "a": 2\n}'
使用制表符(\t)來縮進:
JSON.stringify({ uno: 1, dos : 2 }, null, '\t')
// '{ \
// "uno": 1, \
// "dos": 2 \
// }'
toJSON 方法
如果一個被序列化的對象擁有 toJSON 方法,那么該 toJSON 方法就會覆蓋該對象默認的序列化行為:不是那個對象被序列化,而是調用 toJSON 方法后的返回值會被序列化,例如:
var obj = {
foo: 'foo',
toJSON: function () {
return 'bar';
}
};
JSON.stringify(obj); // '"bar"'
JSON.stringify({x: obj}); // '{"x":"bar"}'
JSON.stringify用作 JavaScript
注意JSON不是javascript嚴格意義上的子集,在JSON中不需要省略兩條終線(Line separator和Paragraph separator)但在JavaScript中需要被省略。因此,如果JSON被用作JSONP時,下面方法可以使用:
function jsFriendlyJSONStringify (s) {
return JSON.stringify(s).
replace(/\u2028/g, '\\u2028').
replace(/\u2029/g, '\\u2029');
}
var s = {
a: String.fromCharCode(0x2028),
b: String.fromCharCode(0x2029)
};
try {
eval('(' + JSON.stringify(s) + ')');
} catch (e) {
console.log(e); // "SyntaxError: unterminated string literal"
}
// No need for a catch
eval('(' + jsFriendlyJSONStringify(s) + ')');
// console.log in Firefox unescapes the Unicode if
// logged to console, so we use alert
alert(jsFriendlyJSONStringify(s)); // {"a":"\u2028","b":"\u2029"}
使用 JSON.stringify 結合 localStorage 的例子
一些時候,你想存儲用戶創建的一個對象,並且,即使在瀏覽器被關閉后仍能恢復該對象。下面的例子是 JSON.stringify 適用於這種情形的一個樣板:
// 創建一個示例數據
var session = {
'screens' : [],
'state' : true
};
session.screens.push({"name":"screenA", "width":450, "height":250});
session.screens.push({"name":"screenB", "width":650, "height":350});
session.screens.push({"name":"screenC", "width":750, "height":120});
session.screens.push({"name":"screenD", "width":250, "height":60});
session.screens.push({"name":"screenE", "width":390, "height":120});
session.screens.push({"name":"screenF", "width":1240, "height":650});
// 使用 JSON.stringify 轉換為 JSON 字符串
// 然后使用 localStorage 保存在 session 名稱里
localStorage.setItem('session', JSON.stringify(session));
// 然后是如何轉換通過 JSON.stringify 生成的字符串,該字符串以 JSON 格式保存在 localStorage 里
var restoredSession = JSON.parse(localStorage.getItem('session'));
// 現在 restoredSession 包含了保存在 localStorage 里的對象
console.log(restoredSession);
Polyfill
JSON 對象不被舊版本瀏覽器支持。你可以把下面代碼放到腳本的開始位置,這樣就可以在那些沒有原生支持 JSON 對象的瀏覽器(比如IE6)中使用 JSON 對象。關於 JSON 對象更復雜且有名的 polyfills 是 JSON2 和 JSON3。
下面的算法用來模擬原生 JSON 對象:
if (!window.JSON) {
window.JSON = {
parse: function(sJSON) { return eval('(' + sJSON + ')'); },
stringify: (function () {
var toString = Object.prototype.toString;
var isArray = Array.isArray || function (a) { return toString.call(a) === '[object Array]'; };
var escMap = {'"': '\\"', '\\': '\\\\', '\b': '\\b', '\f': '\\f', '\n': '\\n', '\r': '\\r', '\t': '\\t'};
var escFunc = function (m) { return escMap[m] || '\\u' + (m.charCodeAt(0) + 0x10000).toString(16).substr(1); };
var escRE = /[\\"\u0000-\u001F\u2028\u2029]/g;
return function stringify(value) {
if (value == null) {
return 'null';
} else if (typeof value === 'number') {
return isFinite(value) ? value.toString() : 'null';
} else if (typeof value === 'boolean') {
return value.toString();
} else if (typeof value === 'object') {
if (typeof value.toJSON === 'function') {
return stringify(value.toJSON());
} else if (isArray(value)) {
var res = '[';
for (var i = 0; i < value.length; i++)
res += (i ? ', ' : '') + stringify(value[i]);
return res + ']';
} else if (toString.call(value) === '[object Object]') {
var tmp = [];
for (var k in value) {
if (value.hasOwnProperty(k))
tmp.push(stringify(k) + ': ' + stringify(value[k]));
}
return '{' + tmp.join(', ') + '}';
}
}
return '"' + value.toString().replace(escRE, escFunc) + '"';
};
})()
};
}