原生JS:JSON對象詳解

JSON對象 支持到IE8,舊版的IE須要Polyfilljavascript

本文參考MDN作的詳細整理,方便你們參考[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript)java

JSON對象自己不能被調用或者做爲構造函數,除了它的這兩個方法屬性外 JSON 對象自己並無什麼有用的功能。git

每一個JSON對象,就是一個值。要麼是簡單類型的值,要麼是複合類型的值,可是隻能是一個值,不能是兩個或更多的值。這就是說,每一個JSON文檔只能包含一個值。github

描述正則表達式

JSON 是一種用來序列化對象、數組、數值、字符串、布爾值和 null 的語法。它基於 JavaScript 語法,可是又有區別:一些 JavaScript 值不是 JSON,而某些 JSON 不是 JavaScript 值。參考 JSON: The JavaScript subset that isn't算法

JavaScript 與 JSON 的區別json

JSON對值的類型和格式有嚴格的規定。數組

  1. 複合類型的值只能是數組或對象,不能是函數、正則表達式對象、日期對象。
  2. 簡單類型的值只有四種:字符串、數值(必須以十進制表示)、布爾值和null(不能使用NaNInfinity-Infinityundefined)。
  3. 字符串必須使用雙引號表示,不能使用單引號。
  4. 對象的鍵名必須放在雙引號裏面。
  5. 數組或對象最後一個成員的後面,不能加逗號。

JavaScript類型瀏覽器

JSON與之區別session

對象和數組

屬性名稱必須用雙引號包裹;最後一個屬性後面不能有逗號。

數值

前導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語法以下:

  1. JSON = null
  2. or true or false
  3. or JSONNumber
  4. or JSONString
  5. or JSONObject
  6. or JSONArray
  7. JSONNumber = - PositiveNumber
  8. or PositiveNumber
  9. PositiveNumber = DecimalNumber
  10. or DecimalNumber . Digits
  11. or DecimalNumber . Digits ExponentPart
  12. or DecimalNumber ExponentPart
  13. DecimalNumber = 0
  14. or OneToNine Digits
  15. ExponentPart = e Exponent
  16. or E Exponent
  17. Exponent = Digits
  18. or + Digits
  19. or - Digits
  20. Digits = Digit
  21. or Digits Digit
  22. Digit = 0 through 9
  23. OneToNine = 1 through 9
  24. JSONString = ""
  25. or " StringCharacters "
  26. StringCharacters = StringCharacter
  27. or StringCharacters StringCharacter
  28. StringCharacter = any character
  29. except " or \\ or U+0000 through U+001F
  30. or EscapeSequence
  31. EscapeSequence = \\" or \\/ or \\\\ or \\b or \\f or \\n or \\r or \\t
  32. or \\u HexDigit HexDigit HexDigit HexDigit
  33. HexDigit = 0 through 9
  34. or A through F
  35. or a through f
  36. JSONObject = { }
  37. or { Members }
  38. Members = JSONString : JSON
  39. or Members , JSONString : JSON
  40. JSONArray = [ ]
  41. or [ ArrayElements ]
  42. ArrayElements = JSON
  43. 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 += (? ', ' : '') + 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) + '"';

      };

    })()

  };

}

相關文章
相關標籤/搜索